summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/__init__.py209
-rw-r--r--lib/sqlalchemy/sql/_dml_constructors.py231
-rw-r--r--lib/sqlalchemy/sql/_elements_constructors.py1637
-rw-r--r--lib/sqlalchemy/sql/_selectable_constructors.py467
-rw-r--r--lib/sqlalchemy/sql/coercions.py124
-rw-r--r--lib/sqlalchemy/sql/default_comparator.py279
-rw-r--r--lib/sqlalchemy/sql/dml.py224
-rw-r--r--lib/sqlalchemy/sql/elements.py1978
-rw-r--r--lib/sqlalchemy/sql/expression.py394
-rw-r--r--lib/sqlalchemy/sql/functions.py2
-rw-r--r--lib/sqlalchemy/sql/operators.py351
-rw-r--r--lib/sqlalchemy/sql/roles.py8
-rw-r--r--lib/sqlalchemy/sql/schema.py112
-rw-r--r--lib/sqlalchemy/sql/selectable.py450
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py60
-rw-r--r--lib/sqlalchemy/sql/type_api.py87
-rw-r--r--lib/sqlalchemy/sql/util.py32
17 files changed, 3889 insertions, 2756 deletions
diff --git a/lib/sqlalchemy/sql/__init__.py b/lib/sqlalchemy/sql/__init__.py
index 9d66c8f61..b558f9bf5 100644
--- a/lib/sqlalchemy/sql/__init__.py
+++ b/lib/sqlalchemy/sql/__init__.py
@@ -5,121 +5,109 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
-from .base import Executable
-from .compiler import COLLECT_CARTESIAN_PRODUCTS
-from .compiler import FROM_LINTING
-from .compiler import NO_LINTING
-from .compiler import WARN_LINTING
-from .expression import Alias
-from .expression import alias
-from .expression import all_
-from .expression import and_
-from .expression import any_
-from .expression import asc
-from .expression import between
-from .expression import bindparam
-from .expression import case
-from .expression import cast
-from .expression import ClauseElement
-from .expression import collate
-from .expression import column
-from .expression import ColumnCollection
-from .expression import ColumnElement
-from .expression import CompoundSelect
-from .expression import cte
-from .expression import Delete
-from .expression import delete
-from .expression import desc
-from .expression import distinct
-from .expression import except_
-from .expression import except_all
-from .expression import exists
-from .expression import extract
-from .expression import false
-from .expression import False_
-from .expression import FromClause
-from .expression import func
-from .expression import funcfilter
-from .expression import Insert
-from .expression import insert
-from .expression import intersect
-from .expression import intersect_all
-from .expression import Join
-from .expression import join
-from .expression import label
-from .expression import LABEL_STYLE_DEFAULT
-from .expression import LABEL_STYLE_DISAMBIGUATE_ONLY
-from .expression import LABEL_STYLE_NONE
-from .expression import LABEL_STYLE_TABLENAME_PLUS_COL
-from .expression import lambda_stmt
-from .expression import LambdaElement
-from .expression import lateral
-from .expression import literal
-from .expression import literal_column
-from .expression import modifier
-from .expression import not_
-from .expression import null
-from .expression import nulls_first
-from .expression import nulls_last
-from .expression import nullsfirst
-from .expression import nullslast
-from .expression import or_
-from .expression import outerjoin
-from .expression import outparam
-from .expression import over
-from .expression import quoted_name
-from .expression import Select
-from .expression import select
-from .expression import Selectable
-from .expression import StatementLambdaElement
-from .expression import Subquery
-from .expression import table
-from .expression import TableClause
-from .expression import TableSample
-from .expression import tablesample
-from .expression import text
-from .expression import true
-from .expression import True_
-from .expression import tuple_
-from .expression import type_coerce
-from .expression import union
-from .expression import union_all
-from .expression import Update
-from .expression import update
-from .expression import Values
-from .expression import values
-from .expression import within_group
-from .visitors import ClauseVisitor
+from .base import Executable as Executable
+from .compiler import COLLECT_CARTESIAN_PRODUCTS as COLLECT_CARTESIAN_PRODUCTS
+from .compiler import FROM_LINTING as FROM_LINTING
+from .compiler import NO_LINTING as NO_LINTING
+from .compiler import WARN_LINTING as WARN_LINTING
+from .expression import Alias as Alias
+from .expression import alias as alias
+from .expression import all_ as all_
+from .expression import and_ as and_
+from .expression import any_ as any_
+from .expression import asc as asc
+from .expression import between as between
+from .expression import bindparam as bindparam
+from .expression import case as case
+from .expression import cast as cast
+from .expression import ClauseElement as ClauseElement
+from .expression import collate as collate
+from .expression import column as column
+from .expression import ColumnCollection as ColumnCollection
+from .expression import ColumnElement as ColumnElement
+from .expression import CompoundSelect as CompoundSelect
+from .expression import cte as cte
+from .expression import Delete as Delete
+from .expression import delete as delete
+from .expression import desc as desc
+from .expression import distinct as distinct
+from .expression import except_ as except_
+from .expression import except_all as except_all
+from .expression import exists as exists
+from .expression import extract as extract
+from .expression import false as false
+from .expression import False_ as False_
+from .expression import FromClause as FromClause
+from .expression import func as func
+from .expression import funcfilter as funcfilter
+from .expression import Insert as Insert
+from .expression import insert as insert
+from .expression import intersect as intersect
+from .expression import intersect_all as intersect_all
+from .expression import Join as Join
+from .expression import join as join
+from .expression import label as label
+from .expression import LABEL_STYLE_DEFAULT as LABEL_STYLE_DEFAULT
+from .expression import lambda_stmt as lambda_stmt
+from .expression import LambdaElement as LambdaElement
+from .expression import lateral as lateral
+from .expression import literal as literal
+from .expression import literal_column as literal_column
+from .expression import modifier as modifier
+from .expression import not_ as not_
+from .expression import null as null
+from .expression import nulls_first as nulls_first
+from .expression import nulls_last as nulls_last
+from .expression import nullsfirst as nullsfirst
+from .expression import nullslast as nullslast
+from .expression import or_ as or_
+from .expression import outerjoin as outerjoin
+from .expression import outparam as outparam
+from .expression import over as over
+from .expression import quoted_name as quoted_name
+from .expression import Select as Select
+from .expression import select as select
+from .expression import Selectable as Selectable
+from .expression import StatementLambdaElement as StatementLambdaElement
+from .expression import Subquery as Subquery
+from .expression import table as table
+from .expression import TableClause as TableClause
+from .expression import TableSample as TableSample
+from .expression import tablesample as tablesample
+from .expression import text as text
+from .expression import true as true
+from .expression import True_ as True_
+from .expression import tuple_ as tuple_
+from .expression import type_coerce as type_coerce
+from .expression import union as union
+from .expression import union_all as union_all
+from .expression import Update as Update
+from .expression import update as update
+from .expression import Values as Values
+from .expression import values as values
+from .expression import within_group as within_group
+from .visitors import ClauseVisitor as ClauseVisitor
-
-def __go(lcls):
- global __all__
- from .. import util as _sa_util
-
- import inspect as _inspect
-
- __all__ = sorted(
- name
- for name, obj in lcls.items()
- if not (name.startswith("_") or _inspect.ismodule(obj))
+if True:
+ # work around zimports bug
+ from .expression import (
+ LABEL_STYLE_DISAMBIGUATE_ONLY as LABEL_STYLE_DISAMBIGUATE_ONLY,
+ )
+ from .expression import LABEL_STYLE_NONE as LABEL_STYLE_NONE
+ from .expression import (
+ LABEL_STYLE_TABLENAME_PLUS_COL as LABEL_STYLE_TABLENAME_PLUS_COL,
)
- from .annotation import _prepare_annotations
- from .annotation import Annotated
- from .elements import AnnotatedColumnElement
- from .elements import ClauseList
- from .selectable import AnnotatedFromClause
- # from .traversals import _preconfigure_traversals
+def __go(lcls):
+ from .. import util as _sa_util
from . import base
from . import coercions
from . import elements
- from . import events
from . import lambdas
from . import selectable
from . import schema
- from . import sqltypes
from . import traversals
from . import type_api
@@ -130,20 +118,19 @@ def __go(lcls):
coercions.lambdas = lambdas
coercions.schema = schema
coercions.selectable = selectable
- coercions.sqltypes = sqltypes
coercions.traversals = traversals
+ from .annotation import _prepare_annotations
+ from .annotation import Annotated
+ from .elements import AnnotatedColumnElement
+ from .elements import ClauseList
+ from .selectable import AnnotatedFromClause
+
_prepare_annotations(ColumnElement, AnnotatedColumnElement)
_prepare_annotations(FromClause, AnnotatedFromClause)
_prepare_annotations(ClauseList, Annotated)
- # this is expensive at import time; elements that are used can create
- # their traversals on demand
- # _preconfigure_traversals(ClauseElement)
-
_sa_util.preloaded.import_prefix("sqlalchemy.sql")
- from . import naming
-
__go(locals())
diff --git a/lib/sqlalchemy/sql/_dml_constructors.py b/lib/sqlalchemy/sql/_dml_constructors.py
new file mode 100644
index 000000000..e62edf5e6
--- /dev/null
+++ b/lib/sqlalchemy/sql/_dml_constructors.py
@@ -0,0 +1,231 @@
+# sql/_dml_constructors.py
+# Copyright (C) 2005-2022 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
+
+from .dml import Delete
+from .dml import Insert
+from .dml import Update
+
+
+def insert(table):
+ """Construct an :class:`_expression.Insert` object.
+
+ E.g.::
+
+ from sqlalchemy import insert
+
+ stmt = (
+ insert(user_table).
+ values(name='username', fullname='Full Username')
+ )
+
+ Similar functionality is available via the
+ :meth:`_expression.TableClause.insert` method on
+ :class:`_schema.Table`.
+
+ .. seealso::
+
+ :ref:`coretutorial_insert_expressions` - in the
+ :ref:`1.x tutorial <sqlexpression_toplevel>`
+
+ :ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
+
+
+ :param table: :class:`_expression.TableClause`
+ which is the subject of the
+ insert.
+
+ :param values: collection of values to be inserted; see
+ :meth:`_expression.Insert.values`
+ for a description of allowed formats here.
+ Can be omitted entirely; a :class:`_expression.Insert` construct
+ will also dynamically render the VALUES clause at execution time
+ based on the parameters passed to :meth:`_engine.Connection.execute`.
+
+ :param inline: if True, no attempt will be made to retrieve the
+ SQL-generated default values to be provided within the statement;
+ in particular,
+ this allows SQL expressions to be rendered 'inline' within the
+ statement without the need to pre-execute them beforehand; for
+ backends that support "returning", this turns off the "implicit
+ returning" feature for the statement.
+
+ If both :paramref:`_expression.Insert.values` and compile-time bind
+ parameters are present, the compile-time bind parameters override the
+ information specified within :paramref:`_expression.Insert.values` on a
+ per-key basis.
+
+ The keys within :paramref:`_expression.Insert.values` can be either
+ :class:`~sqlalchemy.schema.Column` objects or their string
+ identifiers. Each key may reference one of:
+
+ * a literal data value (i.e. string, number, etc.);
+ * a Column object;
+ * a SELECT statement.
+
+ If a ``SELECT`` statement is specified which references this
+ ``INSERT`` statement's table, the statement will be correlated
+ against the ``INSERT`` statement.
+
+ .. seealso::
+
+ :ref:`coretutorial_insert_expressions` - SQL Expression Tutorial
+
+ :ref:`inserts_and_updates` - SQL Expression Tutorial
+
+ """
+ return Insert(table)
+
+
+def update(table):
+ r"""Construct an :class:`_expression.Update` object.
+
+ E.g.::
+
+ from sqlalchemy import update
+
+ stmt = (
+ update(user_table).
+ where(user_table.c.id == 5).
+ values(name='user #5')
+ )
+
+ Similar functionality is available via the
+ :meth:`_expression.TableClause.update` method on
+ :class:`_schema.Table`.
+
+ .. seealso::
+
+ :ref:`inserts_and_updates` - in the
+ :ref:`1.x tutorial <sqlexpression_toplevel>`
+
+ :ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
+
+
+
+ :param table: A :class:`_schema.Table`
+ object representing the database
+ table to be updated.
+
+ :param whereclause: Optional SQL expression describing the ``WHERE``
+ condition of the ``UPDATE`` statement; is equivalent to using the
+ more modern :meth:`~Update.where()` method to specify the ``WHERE``
+ clause.
+
+ :param values:
+ Optional dictionary which specifies the ``SET`` conditions of the
+ ``UPDATE``. If left as ``None``, the ``SET``
+ conditions are determined from those parameters passed to the
+ statement during the execution and/or compilation of the
+ statement. When compiled standalone without any parameters,
+ the ``SET`` clause generates for all columns.
+
+ Modern applications may prefer to use the generative
+ :meth:`_expression.Update.values` method to set the values of the
+ UPDATE statement.
+
+ :param inline:
+ if True, SQL defaults present on :class:`_schema.Column` objects via
+ the ``default`` keyword will be compiled 'inline' into the statement
+ and not pre-executed. This means that their values will not
+ be available in the dictionary returned from
+ :meth:`_engine.CursorResult.last_updated_params`.
+
+ :param preserve_parameter_order: if True, the update statement is
+ expected to receive parameters **only** via the
+ :meth:`_expression.Update.values` method,
+ and they must be passed as a Python
+ ``list`` of 2-tuples. The rendered UPDATE statement will emit the SET
+ clause for each referenced column maintaining this order.
+
+ .. versionadded:: 1.0.10
+
+ .. seealso::
+
+ :ref:`updates_order_parameters` - illustrates the
+ :meth:`_expression.Update.ordered_values` method.
+
+ If both ``values`` and compile-time bind parameters are present, the
+ compile-time bind parameters override the information specified
+ within ``values`` on a per-key basis.
+
+ The keys within ``values`` can be either :class:`_schema.Column`
+ objects or their string identifiers (specifically the "key" of the
+ :class:`_schema.Column`, normally but not necessarily equivalent to
+ its "name"). Normally, the
+ :class:`_schema.Column` objects used here are expected to be
+ part of the target :class:`_schema.Table` that is the table
+ to be updated. However when using MySQL, a multiple-table
+ UPDATE statement can refer to columns from any of
+ the tables referred to in the WHERE clause.
+
+ The values referred to in ``values`` are typically:
+
+ * a literal data value (i.e. string, number, etc.)
+ * a SQL expression, such as a related :class:`_schema.Column`,
+ a scalar-returning :func:`_expression.select` construct,
+ etc.
+
+ When combining :func:`_expression.select` constructs within the
+ values clause of an :func:`_expression.update`
+ construct, the subquery represented
+ by the :func:`_expression.select` should be *correlated* to the
+ parent table, that is, providing criterion which links the table inside
+ the subquery to the outer table being updated::
+
+ users.update().values(
+ name=select(addresses.c.email_address).\
+ where(addresses.c.user_id==users.c.id).\
+ scalar_subquery()
+ )
+
+ .. seealso::
+
+ :ref:`inserts_and_updates` - SQL Expression
+ Language Tutorial
+
+
+ """
+ return Update(table)
+
+
+def delete(table):
+ r"""Construct :class:`_expression.Delete` object.
+
+ E.g.::
+
+ from sqlalchemy import delete
+
+ stmt = (
+ delete(user_table).
+ where(user_table.c.id == 5)
+ )
+
+ Similar functionality is available via the
+ :meth:`_expression.TableClause.delete` method on
+ :class:`_schema.Table`.
+
+ .. seealso::
+
+ :ref:`inserts_and_updates` - in the
+ :ref:`1.x tutorial <sqlexpression_toplevel>`
+
+ :ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
+
+
+ :param table: The table to delete rows from.
+
+ :param whereclause: Optional SQL expression describing the ``WHERE``
+ condition of the ``DELETE`` statement; is equivalent to using the
+ more modern :meth:`~Delete.where()` method to specify the ``WHERE``
+ clause.
+
+ .. seealso::
+
+ :ref:`deletes` - SQL Expression Tutorial
+
+ """
+ return Delete(table)
diff --git a/lib/sqlalchemy/sql/_elements_constructors.py b/lib/sqlalchemy/sql/_elements_constructors.py
new file mode 100644
index 000000000..a8c9372e0
--- /dev/null
+++ b/lib/sqlalchemy/sql/_elements_constructors.py
@@ -0,0 +1,1637 @@
+# sql/_elements_constructors.py
+# Copyright (C) 2005-2022 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
+
+import typing
+from typing import Any
+from typing import cast as _typing_cast
+from typing import Optional
+from typing import overload
+from typing import Type
+from typing import TypeVar
+from typing import Union
+
+from . import coercions
+from . import operators
+from . import roles
+from .base import NO_ARG
+from .coercions import _document_text_coercion
+from .elements import BindParameter
+from .elements import BooleanClauseList
+from .elements import Case
+from .elements import Cast
+from .elements import CollationClause
+from .elements import CollectionAggregate
+from .elements import ColumnClause
+from .elements import ColumnElement
+from .elements import Extract
+from .elements import False_
+from .elements import FunctionFilter
+from .elements import Label
+from .elements import Null
+from .elements import Over
+from .elements import TextClause
+from .elements import True_
+from .elements import Tuple
+from .elements import TypeCoerce
+from .elements import UnaryExpression
+from .elements import WithinGroup
+
+if typing.TYPE_CHECKING:
+ from elements import BinaryExpression
+
+ from . import sqltypes
+ from .functions import FunctionElement
+ from .selectable import FromClause
+ from .type_api import TypeEngine
+
+_T = TypeVar("_T")
+
+
+def all_(expr):
+ """Produce an ALL expression.
+
+ For dialects such as that of PostgreSQL, this operator applies
+ to usage of the :class:`_types.ARRAY` datatype, for that of
+ MySQL, it may apply to a subquery. e.g.::
+
+ # renders on PostgreSQL:
+ # '5 = ALL (somearray)'
+ expr = 5 == all_(mytable.c.somearray)
+
+ # renders on MySQL:
+ # '5 = ALL (SELECT value FROM table)'
+ expr = 5 == all_(select(table.c.value))
+
+ Comparison to NULL may work using ``None``::
+
+ None == all_(mytable.c.somearray)
+
+ The any_() / all_() operators also feature a special "operand flipping"
+ behavior such that if any_() / all_() are used on the left side of a
+ comparison using a standalone operator such as ``==``, ``!=``, etc.
+ (not including operator methods such as
+ :meth:`_sql.ColumnOperators.is_`) the rendered expression is flipped::
+
+ # would render '5 = ALL (column)`
+ all_(mytable.c.column) == 5
+
+ Or with ``None``, which note will not perform
+ the usual step of rendering "IS" as is normally the case for NULL::
+
+ # would render 'NULL = ALL(somearray)'
+ all_(mytable.c.somearray) == None
+
+ .. versionchanged:: 1.4.26 repaired the use of any_() / all_()
+ comparing to NULL on the right side to be flipped to the left.
+
+ The column-level :meth:`_sql.ColumnElement.all_` method (not to be
+ confused with :class:`_types.ARRAY` level
+ :meth:`_types.ARRAY.Comparator.all`) is shorthand for
+ ``all_(col)``::
+
+ 5 == mytable.c.somearray.all_()
+
+ .. seealso::
+
+ :meth:`_sql.ColumnOperators.all_`
+
+ :func:`_expression.any_`
+
+ """
+ return CollectionAggregate._create_all(expr)
+
+
+def and_(*clauses):
+ r"""Produce a conjunction of expressions joined by ``AND``.
+
+ E.g.::
+
+ from sqlalchemy import and_
+
+ stmt = select(users_table).where(
+ and_(
+ users_table.c.name == 'wendy',
+ users_table.c.enrolled == True
+ )
+ )
+
+ The :func:`.and_` conjunction is also available using the
+ Python ``&`` operator (though note that compound expressions
+ need to be parenthesized in order to function with Python
+ operator precedence behavior)::
+
+ stmt = select(users_table).where(
+ (users_table.c.name == 'wendy') &
+ (users_table.c.enrolled == True)
+ )
+
+ The :func:`.and_` operation is also implicit in some cases;
+ the :meth:`_expression.Select.where`
+ method for example can be invoked multiple
+ times against a statement, which will have the effect of each
+ clause being combined using :func:`.and_`::
+
+ stmt = select(users_table).\
+ where(users_table.c.name == 'wendy').\
+ where(users_table.c.enrolled == True)
+
+ The :func:`.and_` construct must be given at least one positional
+ argument in order to be valid; a :func:`.and_` construct with no
+ arguments is ambiguous. To produce an "empty" or dynamically
+ generated :func:`.and_` expression, from a given list of expressions,
+ a "default" element of ``True`` should be specified::
+
+ criteria = and_(True, *expressions)
+
+ The above expression will compile to SQL as the expression ``true``
+ or ``1 = 1``, depending on backend, if no other expressions are
+ present. If expressions are present, then the ``True`` value is
+ ignored as it does not affect the outcome of an AND expression that
+ has other elements.
+
+ .. deprecated:: 1.4 The :func:`.and_` element now requires that at
+ least one argument is passed; creating the :func:`.and_` construct
+ with no arguments is deprecated, and will emit a deprecation warning
+ while continuing to produce a blank SQL string.
+
+ .. seealso::
+
+ :func:`.or_`
+
+ """
+ return BooleanClauseList.and_(*clauses)
+
+
+def any_(expr):
+ """Produce an ANY expression.
+
+ For dialects such as that of PostgreSQL, this operator applies
+ to usage of the :class:`_types.ARRAY` datatype, for that of
+ MySQL, it may apply to a subquery. e.g.::
+
+ # renders on PostgreSQL:
+ # '5 = ANY (somearray)'
+ expr = 5 == any_(mytable.c.somearray)
+
+ # renders on MySQL:
+ # '5 = ANY (SELECT value FROM table)'
+ expr = 5 == any_(select(table.c.value))
+
+ Comparison to NULL may work using ``None`` or :func:`_sql.null`::
+
+ None == any_(mytable.c.somearray)
+
+ The any_() / all_() operators also feature a special "operand flipping"
+ behavior such that if any_() / all_() are used on the left side of a
+ comparison using a standalone operator such as ``==``, ``!=``, etc.
+ (not including operator methods such as
+ :meth:`_sql.ColumnOperators.is_`) the rendered expression is flipped::
+
+ # would render '5 = ANY (column)`
+ any_(mytable.c.column) == 5
+
+ Or with ``None``, which note will not perform
+ the usual step of rendering "IS" as is normally the case for NULL::
+
+ # would render 'NULL = ANY(somearray)'
+ any_(mytable.c.somearray) == None
+
+ .. versionchanged:: 1.4.26 repaired the use of any_() / all_()
+ comparing to NULL on the right side to be flipped to the left.
+
+ The column-level :meth:`_sql.ColumnElement.any_` method (not to be
+ confused with :class:`_types.ARRAY` level
+ :meth:`_types.ARRAY.Comparator.any`) is shorthand for
+ ``any_(col)``::
+
+ 5 = mytable.c.somearray.any_()
+
+ .. seealso::
+
+ :meth:`_sql.ColumnOperators.any_`
+
+ :func:`_expression.all_`
+
+ """
+ return CollectionAggregate._create_any(expr)
+
+
+def asc(column):
+ """Produce an ascending ``ORDER BY`` clause element.
+
+ e.g.::
+
+ from sqlalchemy import asc
+ stmt = select(users_table).order_by(asc(users_table.c.name))
+
+ will produce SQL as::
+
+ SELECT id, name FROM user ORDER BY name ASC
+
+ The :func:`.asc` function is a standalone version of the
+ :meth:`_expression.ColumnElement.asc`
+ method available on all SQL expressions,
+ e.g.::
+
+
+ stmt = select(users_table).order_by(users_table.c.name.asc())
+
+ :param column: A :class:`_expression.ColumnElement` (e.g.
+ scalar SQL expression)
+ with which to apply the :func:`.asc` operation.
+
+ .. seealso::
+
+ :func:`.desc`
+
+ :func:`.nulls_first`
+
+ :func:`.nulls_last`
+
+ :meth:`_expression.Select.order_by`
+
+ """
+ return UnaryExpression._create_asc(column)
+
+
+def collate(expression, collation):
+ """Return the clause ``expression COLLATE collation``.
+
+ e.g.::
+
+ collate(mycolumn, 'utf8_bin')
+
+ produces::
+
+ mycolumn COLLATE utf8_bin
+
+ The collation expression is also quoted if it is a case sensitive
+ identifier, e.g. contains uppercase characters.
+
+ .. versionchanged:: 1.2 quoting is automatically applied to COLLATE
+ expressions if they are case sensitive.
+
+ """
+ return CollationClause._create_collation_expression(expression, collation)
+
+
+def between(expr, lower_bound, upper_bound, symmetric=False):
+ """Produce a ``BETWEEN`` predicate clause.
+
+ E.g.::
+
+ from sqlalchemy import between
+ stmt = select(users_table).where(between(users_table.c.id, 5, 7))
+
+ Would produce SQL resembling::
+
+ SELECT id, name FROM user WHERE id BETWEEN :id_1 AND :id_2
+
+ The :func:`.between` function is a standalone version of the
+ :meth:`_expression.ColumnElement.between` method available on all
+ SQL expressions, as in::
+
+ stmt = select(users_table).where(users_table.c.id.between(5, 7))
+
+ All arguments passed to :func:`.between`, including the left side
+ column expression, are coerced from Python scalar values if a
+ the value is not a :class:`_expression.ColumnElement` subclass.
+ For example,
+ three fixed values can be compared as in::
+
+ print(between(5, 3, 7))
+
+ Which would produce::
+
+ :param_1 BETWEEN :param_2 AND :param_3
+
+ :param expr: a column expression, typically a
+ :class:`_expression.ColumnElement`
+ instance or alternatively a Python scalar expression to be coerced
+ into a column expression, serving as the left side of the ``BETWEEN``
+ expression.
+
+ :param lower_bound: a column or Python scalar expression serving as the
+ lower bound of the right side of the ``BETWEEN`` expression.
+
+ :param upper_bound: a column or Python scalar expression serving as the
+ upper bound of the right side of the ``BETWEEN`` expression.
+
+ :param symmetric: if True, will render " BETWEEN SYMMETRIC ". Note
+ that not all databases support this syntax.
+
+ .. versionadded:: 0.9.5
+
+ .. seealso::
+
+ :meth:`_expression.ColumnElement.between`
+
+ """
+ expr = coercions.expect(roles.ExpressionElementRole, expr)
+ return expr.between(lower_bound, upper_bound, symmetric=symmetric)
+
+
+def outparam(key, type_=None):
+ """Create an 'OUT' parameter for usage in functions (stored procedures),
+ for databases which support them.
+
+ The ``outparam`` can be used like a regular function parameter.
+ The "output" value will be available from the
+ :class:`~sqlalchemy.engine.CursorResult` object via its ``out_parameters``
+ attribute, which returns a dictionary containing the values.
+
+ """
+ return BindParameter(key, None, type_=type_, unique=False, isoutparam=True)
+
+
+@overload
+def not_(clause: "BinaryExpression[_T]") -> "BinaryExpression[_T]":
+ ...
+
+
+@overload
+def not_(clause: "ColumnElement[_T]") -> "UnaryExpression[_T]":
+ ...
+
+
+def not_(clause: "ColumnElement[_T]") -> "ColumnElement[_T]":
+ """Return a negation of the given clause, i.e. ``NOT(clause)``.
+
+ The ``~`` operator is also overloaded on all
+ :class:`_expression.ColumnElement` subclasses to produce the
+ same result.
+
+ """
+
+ return operators.inv(
+ _typing_cast(
+ "ColumnElement[_T]",
+ coercions.expect(roles.ExpressionElementRole, clause),
+ )
+ )
+
+
+def bindparam(
+ key,
+ value=NO_ARG,
+ type_: Optional[Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]] = None,
+ unique=False,
+ required=NO_ARG,
+ quote=None,
+ callable_=None,
+ expanding=False,
+ isoutparam=False,
+ literal_execute=False,
+ _compared_to_operator=None,
+ _compared_to_type=None,
+ _is_crud=False,
+) -> "BindParameter[_T]":
+ r"""Produce a "bound expression".
+
+ The return value is an instance of :class:`.BindParameter`; this
+ is a :class:`_expression.ColumnElement`
+ subclass which represents a so-called
+ "placeholder" value in a SQL expression, the value of which is
+ supplied at the point at which the statement in executed against a
+ database connection.
+
+ In SQLAlchemy, the :func:`.bindparam` construct has
+ the ability to carry along the actual value that will be ultimately
+ used at expression time. In this way, it serves not just as
+ a "placeholder" for eventual population, but also as a means of
+ representing so-called "unsafe" values which should not be rendered
+ directly in a SQL statement, but rather should be passed along
+ to the :term:`DBAPI` as values which need to be correctly escaped
+ and potentially handled for type-safety.
+
+ When using :func:`.bindparam` explicitly, the use case is typically
+ one of traditional deferment of parameters; the :func:`.bindparam`
+ construct accepts a name which can then be referred to at execution
+ time::
+
+ from sqlalchemy import bindparam
+
+ stmt = select(users_table).\
+ where(users_table.c.name == bindparam('username'))
+
+ The above statement, when rendered, will produce SQL similar to::
+
+ SELECT id, name FROM user WHERE name = :username
+
+ In order to populate the value of ``:username`` above, the value
+ would typically be applied at execution time to a method
+ like :meth:`_engine.Connection.execute`::
+
+ result = connection.execute(stmt, username='wendy')
+
+ Explicit use of :func:`.bindparam` is also common when producing
+ UPDATE or DELETE statements that are to be invoked multiple times,
+ where the WHERE criterion of the statement is to change on each
+ invocation, such as::
+
+ stmt = (users_table.update().
+ where(user_table.c.name == bindparam('username')).
+ values(fullname=bindparam('fullname'))
+ )
+
+ connection.execute(
+ stmt, [{"username": "wendy", "fullname": "Wendy Smith"},
+ {"username": "jack", "fullname": "Jack Jones"},
+ ]
+ )
+
+ SQLAlchemy's Core expression system makes wide use of
+ :func:`.bindparam` in an implicit sense. It is typical that Python
+ literal values passed to virtually all SQL expression functions are
+ coerced into fixed :func:`.bindparam` constructs. For example, given
+ a comparison operation such as::
+
+ expr = users_table.c.name == 'Wendy'
+
+ The above expression will produce a :class:`.BinaryExpression`
+ construct, where the left side is the :class:`_schema.Column` object
+ representing the ``name`` column, and the right side is a
+ :class:`.BindParameter` representing the literal value::
+
+ print(repr(expr.right))
+ BindParameter('%(4327771088 name)s', 'Wendy', type_=String())
+
+ The expression above will render SQL such as::
+
+ user.name = :name_1
+
+ Where the ``:name_1`` parameter name is an anonymous name. The
+ actual string ``Wendy`` is not in the rendered string, but is carried
+ along where it is later used within statement execution. If we
+ invoke a statement like the following::
+
+ stmt = select(users_table).where(users_table.c.name == 'Wendy')
+ result = connection.execute(stmt)
+
+ We would see SQL logging output as::
+
+ SELECT "user".id, "user".name
+ FROM "user"
+ WHERE "user".name = %(name_1)s
+ {'name_1': 'Wendy'}
+
+ Above, we see that ``Wendy`` is passed as a parameter to the database,
+ while the placeholder ``:name_1`` is rendered in the appropriate form
+ for the target database, in this case the PostgreSQL database.
+
+ Similarly, :func:`.bindparam` is invoked automatically when working
+ with :term:`CRUD` statements as far as the "VALUES" portion is
+ concerned. The :func:`_expression.insert` construct produces an
+ ``INSERT`` expression which will, at statement execution time, generate
+ bound placeholders based on the arguments passed, as in::
+
+ stmt = users_table.insert()
+ result = connection.execute(stmt, name='Wendy')
+
+ The above will produce SQL output as::
+
+ INSERT INTO "user" (name) VALUES (%(name)s)
+ {'name': 'Wendy'}
+
+ The :class:`_expression.Insert` construct, at
+ compilation/execution time, rendered a single :func:`.bindparam`
+ mirroring the column name ``name`` as a result of the single ``name``
+ parameter we passed to the :meth:`_engine.Connection.execute` method.
+
+ :param key:
+ the key (e.g. the name) for this bind param.
+ Will be used in the generated
+ SQL statement for dialects that use named parameters. This
+ value may be modified when part of a compilation operation,
+ if other :class:`BindParameter` objects exist with the same
+ key, or if its length is too long and truncation is
+ required.
+
+ :param value:
+ Initial value for this bind param. Will be used at statement
+ execution time as the value for this parameter passed to the
+ DBAPI, if no other value is indicated to the statement execution
+ method for this particular parameter name. Defaults to ``None``.
+
+ :param callable\_:
+ A callable function that takes the place of "value". The function
+ will be called at statement execution time to determine the
+ ultimate value. Used for scenarios where the actual bind
+ value cannot be determined at the point at which the clause
+ construct is created, but embedded bind values are still desirable.
+
+ :param type\_:
+ A :class:`.TypeEngine` class or instance representing an optional
+ datatype for this :func:`.bindparam`. If not passed, a type
+ may be determined automatically for the bind, based on the given
+ value; for example, trivial Python types such as ``str``,
+ ``int``, ``bool``
+ may result in the :class:`.String`, :class:`.Integer` or
+ :class:`.Boolean` types being automatically selected.
+
+ The type of a :func:`.bindparam` is significant especially in that
+ the type will apply pre-processing to the value before it is
+ passed to the database. For example, a :func:`.bindparam` which
+ refers to a datetime value, and is specified as holding the
+ :class:`.DateTime` type, may apply conversion needed to the
+ value (such as stringification on SQLite) before passing the value
+ to the database.
+
+ :param unique:
+ if True, the key name of this :class:`.BindParameter` will be
+ modified if another :class:`.BindParameter` of the same name
+ already has been located within the containing
+ expression. This flag is used generally by the internals
+ when producing so-called "anonymous" bound expressions, it
+ isn't generally applicable to explicitly-named :func:`.bindparam`
+ constructs.
+
+ :param required:
+ If ``True``, a value is required at execution time. If not passed,
+ it defaults to ``True`` if neither :paramref:`.bindparam.value`
+ or :paramref:`.bindparam.callable` were passed. If either of these
+ parameters are present, then :paramref:`.bindparam.required`
+ defaults to ``False``.
+
+ :param quote:
+ True if this parameter name requires quoting and is not
+ currently known as a SQLAlchemy reserved word; this currently
+ only applies to the Oracle backend, where bound names must
+ sometimes be quoted.
+
+ :param isoutparam:
+ if True, the parameter should be treated like a stored procedure
+ "OUT" parameter. This applies to backends such as Oracle which
+ support OUT parameters.
+
+ :param expanding:
+ if True, this parameter will be treated as an "expanding" parameter
+ at execution time; the parameter value is expected to be a sequence,
+ rather than a scalar value, and the string SQL statement will
+ be transformed on a per-execution basis to accommodate the sequence
+ with a variable number of parameter slots passed to the DBAPI.
+ This is to allow statement caching to be used in conjunction with
+ an IN clause.
+
+ .. seealso::
+
+ :meth:`.ColumnOperators.in_`
+
+ :ref:`baked_in` - with baked queries
+
+ .. note:: The "expanding" feature does not support "executemany"-
+ style parameter sets.
+
+ .. versionadded:: 1.2
+
+ .. versionchanged:: 1.3 the "expanding" bound parameter feature now
+ supports empty lists.
+
+
+ .. seealso::
+
+ :ref:`coretutorial_bind_param`
+
+ :ref:`coretutorial_insert_expressions`
+
+ :func:`.outparam`
+
+ :param literal_execute:
+ if True, the bound parameter will be rendered in the compile phase
+ with a special "POSTCOMPILE" token, and the SQLAlchemy compiler will
+ render the final value of the parameter into the SQL statement at
+ statement execution time, omitting the value from the parameter
+ dictionary / list passed to DBAPI ``cursor.execute()``. This
+ produces a similar effect as that of using the ``literal_binds``,
+ compilation flag, however takes place as the statement is sent to
+ the DBAPI ``cursor.execute()`` method, rather than when the statement
+ is compiled. The primary use of this
+ capability is for rendering LIMIT / OFFSET clauses for database
+ drivers that can't accommodate for bound parameters in these
+ contexts, while allowing SQL constructs to be cacheable at the
+ compilation level.
+
+ .. versionadded:: 1.4 Added "post compile" bound parameters
+
+ .. seealso::
+
+ :ref:`change_4808`.
+
+ """
+ return BindParameter(
+ key,
+ value,
+ type_,
+ unique,
+ required,
+ quote,
+ callable_,
+ expanding,
+ isoutparam,
+ literal_execute,
+ _compared_to_operator,
+ _compared_to_type,
+ _is_crud,
+ )
+
+
+def case(*whens, value=None, else_=None) -> "Case[Any]":
+ r"""Produce a ``CASE`` expression.
+
+ The ``CASE`` construct in SQL is a conditional object that
+ acts somewhat analogously to an "if/then" construct in other
+ languages. It returns an instance of :class:`.Case`.
+
+ :func:`.case` in its usual form is passed a series of "when"
+ constructs, that is, a list of conditions and results as tuples::
+
+ from sqlalchemy import case
+
+ stmt = select(users_table).\
+ where(
+ case(
+ (users_table.c.name == 'wendy', 'W'),
+ (users_table.c.name == 'jack', 'J'),
+ else_='E'
+ )
+ )
+
+ The above statement will produce SQL resembling::
+
+ SELECT id, name FROM user
+ WHERE CASE
+ WHEN (name = :name_1) THEN :param_1
+ WHEN (name = :name_2) THEN :param_2
+ ELSE :param_3
+ END
+
+ When simple equality expressions of several values against a single
+ parent column are needed, :func:`.case` also has a "shorthand" format
+ used via the
+ :paramref:`.case.value` parameter, which is passed a column
+ expression to be compared. In this form, the :paramref:`.case.whens`
+ parameter is passed as a dictionary containing expressions to be
+ compared against keyed to result expressions. The statement below is
+ equivalent to the preceding statement::
+
+ stmt = select(users_table).\
+ where(
+ case(
+ {"wendy": "W", "jack": "J"},
+ value=users_table.c.name,
+ else_='E'
+ )
+ )
+
+ The values which are accepted as result values in
+ :paramref:`.case.whens` as well as with :paramref:`.case.else_` are
+ coerced from Python literals into :func:`.bindparam` constructs.
+ SQL expressions, e.g. :class:`_expression.ColumnElement` constructs,
+ are accepted
+ as well. To coerce a literal string expression into a constant
+ expression rendered inline, use the :func:`_expression.literal_column`
+ construct,
+ as in::
+
+ from sqlalchemy import case, literal_column
+
+ case(
+ (
+ orderline.c.qty > 100,
+ literal_column("'greaterthan100'")
+ ),
+ (
+ orderline.c.qty > 10,
+ literal_column("'greaterthan10'")
+ ),
+ else_=literal_column("'lessthan10'")
+ )
+
+ The above will render the given constants without using bound
+ parameters for the result values (but still for the comparison
+ values), as in::
+
+ CASE
+ WHEN (orderline.qty > :qty_1) THEN 'greaterthan100'
+ WHEN (orderline.qty > :qty_2) THEN 'greaterthan10'
+ ELSE 'lessthan10'
+ END
+
+ :param \*whens: The criteria to be compared against,
+ :paramref:`.case.whens` accepts two different forms, based on
+ whether or not :paramref:`.case.value` is used.
+
+ .. versionchanged:: 1.4 the :func:`_sql.case`
+ function now accepts the series of WHEN conditions positionally
+
+ In the first form, it accepts a list of 2-tuples; each 2-tuple
+ consists of ``(<sql expression>, <value>)``, where the SQL
+ expression is a boolean expression and "value" is a resulting value,
+ e.g.::
+
+ case(
+ (users_table.c.name == 'wendy', 'W'),
+ (users_table.c.name == 'jack', 'J')
+ )
+
+ In the second form, it accepts a Python dictionary of comparison
+ values mapped to a resulting value; this form requires
+ :paramref:`.case.value` to be present, and values will be compared
+ using the ``==`` operator, e.g.::
+
+ case(
+ {"wendy": "W", "jack": "J"},
+ value=users_table.c.name
+ )
+
+ :param value: An optional SQL expression which will be used as a
+ fixed "comparison point" for candidate values within a dictionary
+ passed to :paramref:`.case.whens`.
+
+ :param else\_: An optional SQL expression which will be the evaluated
+ result of the ``CASE`` construct if all expressions within
+ :paramref:`.case.whens` evaluate to false. When omitted, most
+ databases will produce a result of NULL if none of the "when"
+ expressions evaluate to true.
+
+
+ """
+ return Case(*whens, value=value, else_=else_)
+
+
+def cast(
+ expression: ColumnElement,
+ type_: Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"],
+) -> "Cast[_T]":
+ r"""Produce a ``CAST`` expression.
+
+ :func:`.cast` returns an instance of :class:`.Cast`.
+
+ E.g.::
+
+ from sqlalchemy import cast, Numeric
+
+ stmt = select(cast(product_table.c.unit_price, Numeric(10, 4)))
+
+ The above statement will produce SQL resembling::
+
+ SELECT CAST(unit_price AS NUMERIC(10, 4)) FROM product
+
+ The :func:`.cast` function performs two distinct functions when
+ used. The first is that it renders the ``CAST`` expression within
+ the resulting SQL string. The second is that it associates the given
+ type (e.g. :class:`.TypeEngine` class or instance) with the column
+ expression on the Python side, which means the expression will take
+ on the expression operator behavior associated with that type,
+ as well as the bound-value handling and result-row-handling behavior
+ of the type.
+
+ .. versionchanged:: 0.9.0 :func:`.cast` now applies the given type
+ to the expression such that it takes effect on the bound-value,
+ e.g. the Python-to-database direction, in addition to the
+ result handling, e.g. database-to-Python, direction.
+
+ An alternative to :func:`.cast` is the :func:`.type_coerce` function.
+ This function performs the second task of associating an expression
+ with a specific type, but does not render the ``CAST`` expression
+ in SQL.
+
+ :param expression: A SQL expression, such as a
+ :class:`_expression.ColumnElement`
+ expression or a Python string which will be coerced into a bound
+ literal value.
+
+ :param type\_: A :class:`.TypeEngine` class or instance indicating
+ the type to which the ``CAST`` should apply.
+
+ .. seealso::
+
+ :ref:`coretutorial_casts`
+
+ :func:`.type_coerce` - an alternative to CAST that coerces the type
+ on the Python side only, which is often sufficient to generate the
+ correct SQL and data coercion.
+
+
+ """
+ return Cast(expression, type_)
+
+
+def column(
+ text: str,
+ type_: Optional[Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]] = None,
+ is_literal: bool = False,
+ _selectable: Optional["FromClause"] = None,
+) -> "ColumnClause[_T]":
+ """Produce a :class:`.ColumnClause` object.
+
+ The :class:`.ColumnClause` is a lightweight analogue to the
+ :class:`_schema.Column` class. The :func:`_expression.column`
+ function can
+ be invoked with just a name alone, as in::
+
+ from sqlalchemy import column
+
+ id, name = column("id"), column("name")
+ stmt = select(id, name).select_from("user")
+
+ The above statement would produce SQL like::
+
+ SELECT id, name FROM user
+
+ Once constructed, :func:`_expression.column`
+ may be used like any other SQL
+ expression element such as within :func:`_expression.select`
+ constructs::
+
+ from sqlalchemy.sql import column
+
+ id, name = column("id"), column("name")
+ stmt = select(id, name).select_from("user")
+
+ The text handled by :func:`_expression.column`
+ is assumed to be handled
+ like the name of a database column; if the string contains mixed case,
+ special characters, or matches a known reserved word on the target
+ backend, the column expression will render using the quoting
+ behavior determined by the backend. To produce a textual SQL
+ expression that is rendered exactly without any quoting,
+ use :func:`_expression.literal_column` instead,
+ or pass ``True`` as the
+ value of :paramref:`_expression.column.is_literal`. Additionally,
+ full SQL
+ statements are best handled using the :func:`_expression.text`
+ construct.
+
+ :func:`_expression.column` can be used in a table-like
+ fashion by combining it with the :func:`.table` function
+ (which is the lightweight analogue to :class:`_schema.Table`
+ ) to produce
+ a working table construct with minimal boilerplate::
+
+ from sqlalchemy import table, column, select
+
+ user = table("user",
+ column("id"),
+ column("name"),
+ column("description"),
+ )
+
+ stmt = select(user.c.description).where(user.c.name == 'wendy')
+
+ A :func:`_expression.column` / :func:`.table`
+ construct like that illustrated
+ above can be created in an
+ ad-hoc fashion and is not associated with any
+ :class:`_schema.MetaData`, DDL, or events, unlike its
+ :class:`_schema.Table` counterpart.
+
+ .. versionchanged:: 1.0.0 :func:`_expression.column` can now
+ be imported from the plain ``sqlalchemy`` namespace like any
+ other SQL element.
+
+ :param text: the text of the element.
+
+ :param type: :class:`_types.TypeEngine` object which can associate
+ this :class:`.ColumnClause` with a type.
+
+ :param is_literal: if True, the :class:`.ColumnClause` is assumed to
+ be an exact expression that will be delivered to the output with no
+ quoting rules applied regardless of case sensitive settings. the
+ :func:`_expression.literal_column()` function essentially invokes
+ :func:`_expression.column` while passing ``is_literal=True``.
+
+ .. seealso::
+
+ :class:`_schema.Column`
+
+ :func:`_expression.literal_column`
+
+ :func:`.table`
+
+ :func:`_expression.text`
+
+ :ref:`sqlexpression_literal_column`
+
+ """
+ self = ColumnClause.__new__(ColumnClause)
+ self.__init__(text, type_, is_literal, _selectable)
+ return self
+
+
+def desc(column):
+ """Produce a descending ``ORDER BY`` clause element.
+
+ e.g.::
+
+ from sqlalchemy import desc
+
+ stmt = select(users_table).order_by(desc(users_table.c.name))
+
+ will produce SQL as::
+
+ SELECT id, name FROM user ORDER BY name DESC
+
+ The :func:`.desc` function is a standalone version of the
+ :meth:`_expression.ColumnElement.desc`
+ method available on all SQL expressions,
+ e.g.::
+
+
+ stmt = select(users_table).order_by(users_table.c.name.desc())
+
+ :param column: A :class:`_expression.ColumnElement` (e.g.
+ scalar SQL expression)
+ with which to apply the :func:`.desc` operation.
+
+ .. seealso::
+
+ :func:`.asc`
+
+ :func:`.nulls_first`
+
+ :func:`.nulls_last`
+
+ :meth:`_expression.Select.order_by`
+
+ """
+ return UnaryExpression._create_desc(column)
+
+
+def distinct(expr):
+ """Produce an column-expression-level unary ``DISTINCT`` clause.
+
+ This applies the ``DISTINCT`` keyword to an individual column
+ expression, and is typically contained within an aggregate function,
+ as in::
+
+ from sqlalchemy import distinct, func
+ stmt = select(func.count(distinct(users_table.c.name)))
+
+ The above would produce an expression resembling::
+
+ SELECT COUNT(DISTINCT name) FROM user
+
+ The :func:`.distinct` function is also available as a column-level
+ method, e.g. :meth:`_expression.ColumnElement.distinct`, as in::
+
+ stmt = select(func.count(users_table.c.name.distinct()))
+
+ The :func:`.distinct` operator is different from the
+ :meth:`_expression.Select.distinct` method of
+ :class:`_expression.Select`,
+ which produces a ``SELECT`` statement
+ with ``DISTINCT`` applied to the result set as a whole,
+ e.g. a ``SELECT DISTINCT`` expression. See that method for further
+ information.
+
+ .. seealso::
+
+ :meth:`_expression.ColumnElement.distinct`
+
+ :meth:`_expression.Select.distinct`
+
+ :data:`.func`
+
+ """
+ return UnaryExpression._create_distinct(expr)
+
+
+def extract(field: str, expr: ColumnElement) -> "Extract[sqltypes.Integer]":
+ """Return a :class:`.Extract` construct.
+
+ This is typically available as :func:`.extract`
+ as well as ``func.extract`` from the
+ :data:`.func` namespace.
+
+ :param field: The field to extract.
+
+ :param expr: A column or Python scalar expression serving as the
+ right side of the ``EXTRACT`` expression.
+
+ E.g.::
+
+ from sqlalchemy import extract
+ from sqlalchemy import table, column
+
+ logged_table = table("user",
+ column("id"),
+ column("date_created"),
+ )
+
+ stmt = select(logged_table.c.id).where(
+ extract("YEAR", logged_table.c.date_created) == 2021
+ )
+
+ In the above example, the statement is used to select ids from the
+ database where the ``YEAR`` component matches a specific value.
+
+ Similarly, one can also select an extracted component::
+
+ stmt = select(
+ extract("YEAR", logged_table.c.date_created)
+ ).where(logged_table.c.id == 1)
+
+ The implementation of ``EXTRACT`` may vary across database backends.
+ Users are reminded to consult their database documentation.
+ """
+ return Extract(field, expr)
+
+
+def false():
+ """Return a :class:`.False_` construct.
+
+ E.g.::
+
+ >>> from sqlalchemy import false
+ >>> print(select(t.c.x).where(false()))
+ SELECT x FROM t WHERE false
+
+ A backend which does not support true/false constants will render as
+ an expression against 1 or 0::
+
+ >>> print(select(t.c.x).where(false()))
+ SELECT x FROM t WHERE 0 = 1
+
+ The :func:`.true` and :func:`.false` constants also feature
+ "short circuit" operation within an :func:`.and_` or :func:`.or_`
+ conjunction::
+
+ >>> print(select(t.c.x).where(or_(t.c.x > 5, true())))
+ SELECT x FROM t WHERE true
+
+ >>> print(select(t.c.x).where(and_(t.c.x > 5, false())))
+ SELECT x FROM t WHERE false
+
+ .. versionchanged:: 0.9 :func:`.true` and :func:`.false` feature
+ better integrated behavior within conjunctions and on dialects
+ that don't support true/false constants.
+
+ .. seealso::
+
+ :func:`.true`
+
+ """
+
+ return False_._instance()
+
+
+def funcfilter(func, *criterion) -> "FunctionFilter":
+ """Produce a :class:`.FunctionFilter` object against a function.
+
+ Used against aggregate and window functions,
+ for database backends that support the "FILTER" clause.
+
+ E.g.::
+
+ from sqlalchemy import funcfilter
+ funcfilter(func.count(1), MyClass.name == 'some name')
+
+ Would produce "COUNT(1) FILTER (WHERE myclass.name = 'some name')".
+
+ This function is also available from the :data:`~.expression.func`
+ construct itself via the :meth:`.FunctionElement.filter` method.
+
+ .. versionadded:: 1.0.0
+
+ .. seealso::
+
+ :ref:`tutorial_functions_within_group` - in the
+ :ref:`unified_tutorial`
+
+ :meth:`.FunctionElement.filter`
+
+ """
+ return FunctionFilter(func, *criterion)
+
+
+def label(
+ name: str,
+ element: ColumnElement[_T],
+ type_: Optional[Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]] = None,
+) -> "Label[_T]":
+ """Return a :class:`Label` object for the
+ given :class:`_expression.ColumnElement`.
+
+ A label changes the name of an element in the columns clause of a
+ ``SELECT`` statement, typically via the ``AS`` SQL keyword.
+
+ This functionality is more conveniently available via the
+ :meth:`_expression.ColumnElement.label` method on
+ :class:`_expression.ColumnElement`.
+
+ :param name: label name
+
+ :param obj: a :class:`_expression.ColumnElement`.
+
+ """
+ return Label(name, element, type_)
+
+
+def null():
+ """Return a constant :class:`.Null` construct."""
+
+ return Null._instance()
+
+
+def nulls_first(column):
+ """Produce the ``NULLS FIRST`` modifier for an ``ORDER BY`` expression.
+
+ :func:`.nulls_first` is intended to modify the expression produced
+ by :func:`.asc` or :func:`.desc`, and indicates how NULL values
+ should be handled when they are encountered during ordering::
+
+
+ from sqlalchemy import desc, nulls_first
+
+ stmt = select(users_table).order_by(
+ nulls_first(desc(users_table.c.name)))
+
+ The SQL expression from the above would resemble::
+
+ SELECT id, name FROM user ORDER BY name DESC NULLS FIRST
+
+ Like :func:`.asc` and :func:`.desc`, :func:`.nulls_first` is typically
+ invoked from the column expression itself using
+ :meth:`_expression.ColumnElement.nulls_first`,
+ rather than as its standalone
+ function version, as in::
+
+ stmt = select(users_table).order_by(
+ users_table.c.name.desc().nulls_first())
+
+ .. versionchanged:: 1.4 :func:`.nulls_first` is renamed from
+ :func:`.nullsfirst` in previous releases.
+ The previous name remains available for backwards compatibility.
+
+ .. seealso::
+
+ :func:`.asc`
+
+ :func:`.desc`
+
+ :func:`.nulls_last`
+
+ :meth:`_expression.Select.order_by`
+
+ """
+ return UnaryExpression._create_nulls_first(column)
+
+
+def nulls_last(column):
+ """Produce the ``NULLS LAST`` modifier for an ``ORDER BY`` expression.
+
+ :func:`.nulls_last` is intended to modify the expression produced
+ by :func:`.asc` or :func:`.desc`, and indicates how NULL values
+ should be handled when they are encountered during ordering::
+
+
+ from sqlalchemy import desc, nulls_last
+
+ stmt = select(users_table).order_by(
+ nulls_last(desc(users_table.c.name)))
+
+ The SQL expression from the above would resemble::
+
+ SELECT id, name FROM user ORDER BY name DESC NULLS LAST
+
+ Like :func:`.asc` and :func:`.desc`, :func:`.nulls_last` is typically
+ invoked from the column expression itself using
+ :meth:`_expression.ColumnElement.nulls_last`,
+ rather than as its standalone
+ function version, as in::
+
+ stmt = select(users_table).order_by(
+ users_table.c.name.desc().nulls_last())
+
+ .. versionchanged:: 1.4 :func:`.nulls_last` is renamed from
+ :func:`.nullslast` in previous releases.
+ The previous name remains available for backwards compatibility.
+
+ .. seealso::
+
+ :func:`.asc`
+
+ :func:`.desc`
+
+ :func:`.nulls_first`
+
+ :meth:`_expression.Select.order_by`
+
+ """
+ return UnaryExpression._create_nulls_last(column)
+
+
+def or_(*clauses):
+ """Produce a conjunction of expressions joined by ``OR``.
+
+ E.g.::
+
+ from sqlalchemy import or_
+
+ stmt = select(users_table).where(
+ or_(
+ users_table.c.name == 'wendy',
+ users_table.c.name == 'jack'
+ )
+ )
+
+ The :func:`.or_` conjunction is also available using the
+ Python ``|`` operator (though note that compound expressions
+ need to be parenthesized in order to function with Python
+ operator precedence behavior)::
+
+ stmt = select(users_table).where(
+ (users_table.c.name == 'wendy') |
+ (users_table.c.name == 'jack')
+ )
+
+ The :func:`.or_` construct must be given at least one positional
+ argument in order to be valid; a :func:`.or_` construct with no
+ arguments is ambiguous. To produce an "empty" or dynamically
+ generated :func:`.or_` expression, from a given list of expressions,
+ a "default" element of ``False`` should be specified::
+
+ or_criteria = or_(False, *expressions)
+
+ The above expression will compile to SQL as the expression ``false``
+ or ``0 = 1``, depending on backend, if no other expressions are
+ present. If expressions are present, then the ``False`` value is
+ ignored as it does not affect the outcome of an OR expression which
+ has other elements.
+
+ .. deprecated:: 1.4 The :func:`.or_` element now requires that at
+ least one argument is passed; creating the :func:`.or_` construct
+ with no arguments is deprecated, and will emit a deprecation warning
+ while continuing to produce a blank SQL string.
+
+ .. seealso::
+
+ :func:`.and_`
+
+ """
+ return BooleanClauseList.or_(*clauses)
+
+
+def over(
+ element: "FunctionElement[_T]",
+ partition_by=None,
+ order_by=None,
+ range_=None,
+ rows=None,
+) -> "Over[_T]":
+ r"""Produce an :class:`.Over` object against a function.
+
+ Used against aggregate or so-called "window" functions,
+ for database backends that support window functions.
+
+ :func:`_expression.over` is usually called using
+ the :meth:`.FunctionElement.over` method, e.g.::
+
+ func.row_number().over(order_by=mytable.c.some_column)
+
+ Would produce::
+
+ ROW_NUMBER() OVER(ORDER BY some_column)
+
+ Ranges are also possible using the :paramref:`.expression.over.range_`
+ and :paramref:`.expression.over.rows` parameters. These
+ mutually-exclusive parameters each accept a 2-tuple, which contains
+ a combination of integers and None::
+
+ func.row_number().over(
+ order_by=my_table.c.some_column, range_=(None, 0))
+
+ The above would produce::
+
+ ROW_NUMBER() OVER(ORDER BY some_column
+ RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+
+ A value of ``None`` indicates "unbounded", a
+ value of zero indicates "current row", and negative / positive
+ integers indicate "preceding" and "following":
+
+ * RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING::
+
+ func.row_number().over(order_by='x', range_=(-5, 10))
+
+ * ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW::
+
+ func.row_number().over(order_by='x', rows=(None, 0))
+
+ * RANGE BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING::
+
+ func.row_number().over(order_by='x', range_=(-2, None))
+
+ * RANGE BETWEEN 1 FOLLOWING AND 3 FOLLOWING::
+
+ func.row_number().over(order_by='x', range_=(1, 3))
+
+ .. versionadded:: 1.1 support for RANGE / ROWS within a window
+
+
+ :param element: a :class:`.FunctionElement`, :class:`.WithinGroup`,
+ or other compatible construct.
+ :param partition_by: a column element or string, or a list
+ of such, that will be used as the PARTITION BY clause
+ of the OVER construct.
+ :param order_by: a column element or string, or a list
+ of such, that will be used as the ORDER BY clause
+ of the OVER construct.
+ :param range\_: optional range clause for the window. This is a
+ tuple value which can contain integer values or ``None``,
+ and will render a RANGE BETWEEN PRECEDING / FOLLOWING clause.
+
+ .. versionadded:: 1.1
+
+ :param rows: optional rows clause for the window. This is a tuple
+ value which can contain integer values or None, and will render
+ a ROWS BETWEEN PRECEDING / FOLLOWING clause.
+
+ .. versionadded:: 1.1
+
+ This function is also available from the :data:`~.expression.func`
+ construct itself via the :meth:`.FunctionElement.over` method.
+
+ .. seealso::
+
+ :ref:`tutorial_window_functions` - in the :ref:`unified_tutorial`
+
+ :data:`.expression.func`
+
+ :func:`_expression.within_group`
+
+ """
+ return Over(element, partition_by, order_by, range_, rows)
+
+
+@_document_text_coercion("text", ":func:`.text`", ":paramref:`.text.text`")
+def text(text):
+ r"""Construct a new :class:`_expression.TextClause` clause,
+ representing
+ a textual SQL string directly.
+
+ E.g.::
+
+ from sqlalchemy import text
+
+ t = text("SELECT * FROM users")
+ result = connection.execute(t)
+
+ The advantages :func:`_expression.text`
+ provides over a plain string are
+ backend-neutral support for bind parameters, per-statement
+ execution options, as well as
+ bind parameter and result-column typing behavior, allowing
+ SQLAlchemy type constructs to play a role when executing
+ a statement that is specified literally. The construct can also
+ be provided with a ``.c`` collection of column elements, allowing
+ it to be embedded in other SQL expression constructs as a subquery.
+
+ Bind parameters are specified by name, using the format ``:name``.
+ E.g.::
+
+ t = text("SELECT * FROM users WHERE id=:user_id")
+ result = connection.execute(t, user_id=12)
+
+ For SQL statements where a colon is required verbatim, as within
+ an inline string, use a backslash to escape::
+
+ t = text("SELECT * FROM users WHERE name='\:username'")
+
+ The :class:`_expression.TextClause`
+ construct includes methods which can
+ provide information about the bound parameters as well as the column
+ values which would be returned from the textual statement, assuming
+ it's an executable SELECT type of statement. The
+ :meth:`_expression.TextClause.bindparams`
+ method is used to provide bound
+ parameter detail, and :meth:`_expression.TextClause.columns`
+ method allows
+ specification of return columns including names and types::
+
+ t = text("SELECT * FROM users WHERE id=:user_id").\
+ bindparams(user_id=7).\
+ columns(id=Integer, name=String)
+
+ for id, name in connection.execute(t):
+ print(id, name)
+
+ The :func:`_expression.text` construct is used in cases when
+ a literal string SQL fragment is specified as part of a larger query,
+ such as for the WHERE clause of a SELECT statement::
+
+ s = select(users.c.id, users.c.name).where(text("id=:user_id"))
+ result = connection.execute(s, user_id=12)
+
+ :func:`_expression.text` is also used for the construction
+ of a full, standalone statement using plain text.
+ As such, SQLAlchemy refers
+ to it as an :class:`.Executable` object and may be used
+ like any other statement passed to an ``.execute()`` method.
+
+ :param text:
+ the text of the SQL statement to be created. Use ``:<param>``
+ to specify bind parameters; they will be compiled to their
+ engine-specific format.
+
+ .. seealso::
+
+ :ref:`sqlexpression_text` - in the Core tutorial
+
+
+ """
+ return TextClause(text)
+
+
+def true():
+ """Return a constant :class:`.True_` construct.
+
+ E.g.::
+
+ >>> from sqlalchemy import true
+ >>> print(select(t.c.x).where(true()))
+ SELECT x FROM t WHERE true
+
+ A backend which does not support true/false constants will render as
+ an expression against 1 or 0::
+
+ >>> print(select(t.c.x).where(true()))
+ SELECT x FROM t WHERE 1 = 1
+
+ The :func:`.true` and :func:`.false` constants also feature
+ "short circuit" operation within an :func:`.and_` or :func:`.or_`
+ conjunction::
+
+ >>> print(select(t.c.x).where(or_(t.c.x > 5, true())))
+ SELECT x FROM t WHERE true
+
+ >>> print(select(t.c.x).where(and_(t.c.x > 5, false())))
+ SELECT x FROM t WHERE false
+
+ .. versionchanged:: 0.9 :func:`.true` and :func:`.false` feature
+ better integrated behavior within conjunctions and on dialects
+ that don't support true/false constants.
+
+ .. seealso::
+
+ :func:`.false`
+
+ """
+
+ return True_._instance()
+
+
+def tuple_(*clauses: roles.ExpressionElementRole, types=None) -> "Tuple":
+ """Return a :class:`.Tuple`.
+
+ Main usage is to produce a composite IN construct using
+ :meth:`.ColumnOperators.in_` ::
+
+ from sqlalchemy import tuple_
+
+ tuple_(table.c.col1, table.c.col2).in_(
+ [(1, 2), (5, 12), (10, 19)]
+ )
+
+ .. versionchanged:: 1.3.6 Added support for SQLite IN tuples.
+
+ .. warning::
+
+ The composite IN construct is not supported by all backends, and is
+ currently known to work on PostgreSQL, MySQL, and SQLite.
+ Unsupported backends will raise a subclass of
+ :class:`~sqlalchemy.exc.DBAPIError` when such an expression is
+ invoked.
+
+ """
+ return Tuple(*clauses, types=types)
+
+
+def type_coerce(
+ expression: "ColumnElement",
+ type_: Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"],
+) -> "TypeCoerce[_T]":
+ r"""Associate a SQL expression with a particular type, without rendering
+ ``CAST``.
+
+ E.g.::
+
+ from sqlalchemy import type_coerce
+
+ stmt = select(type_coerce(log_table.date_string, StringDateTime()))
+
+ The above construct will produce a :class:`.TypeCoerce` object, which
+ does not modify the rendering in any way on the SQL side, with the
+ possible exception of a generated label if used in a columns clause
+ context::
+
+ SELECT date_string AS date_string FROM log
+
+ When result rows are fetched, the ``StringDateTime`` type processor
+ will be applied to result rows on behalf of the ``date_string`` column.
+
+ .. note:: the :func:`.type_coerce` construct does not render any
+ SQL syntax of its own, including that it does not imply
+ parenthesization. Please use :meth:`.TypeCoerce.self_group`
+ if explicit parenthesization is required.
+
+ In order to provide a named label for the expression, use
+ :meth:`_expression.ColumnElement.label`::
+
+ stmt = select(
+ type_coerce(log_table.date_string, StringDateTime()).label('date')
+ )
+
+
+ A type that features bound-value handling will also have that behavior
+ take effect when literal values or :func:`.bindparam` constructs are
+ passed to :func:`.type_coerce` as targets.
+ For example, if a type implements the
+ :meth:`.TypeEngine.bind_expression`
+ method or :meth:`.TypeEngine.bind_processor` method or equivalent,
+ these functions will take effect at statement compilation/execution
+ time when a literal value is passed, as in::
+
+ # bound-value handling of MyStringType will be applied to the
+ # literal value "some string"
+ stmt = select(type_coerce("some string", MyStringType))
+
+ When using :func:`.type_coerce` with composed expressions, note that
+ **parenthesis are not applied**. If :func:`.type_coerce` is being
+ used in an operator context where the parenthesis normally present from
+ CAST are necessary, use the :meth:`.TypeCoerce.self_group` method::
+
+ >>> some_integer = column("someint", Integer)
+ >>> some_string = column("somestr", String)
+ >>> expr = type_coerce(some_integer + 5, String) + some_string
+ >>> print(expr)
+ someint + :someint_1 || somestr
+ >>> expr = type_coerce(some_integer + 5, String).self_group() + some_string
+ >>> print(expr)
+ (someint + :someint_1) || somestr
+
+ :param expression: A SQL expression, such as a
+ :class:`_expression.ColumnElement`
+ expression or a Python string which will be coerced into a bound
+ literal value.
+
+ :param type\_: A :class:`.TypeEngine` class or instance indicating
+ the type to which the expression is coerced.
+
+ .. seealso::
+
+ :ref:`coretutorial_casts`
+
+ :func:`.cast`
+
+ """ # noqa
+ return TypeCoerce(expression, type_)
+
+
+def within_group(
+ element: "FunctionElement[_T]", *order_by: roles.OrderByRole
+) -> "WithinGroup[_T]":
+ r"""Produce a :class:`.WithinGroup` object against a function.
+
+ Used against so-called "ordered set aggregate" and "hypothetical
+ set aggregate" functions, including :class:`.percentile_cont`,
+ :class:`.rank`, :class:`.dense_rank`, etc.
+
+ :func:`_expression.within_group` is usually called using
+ the :meth:`.FunctionElement.within_group` method, e.g.::
+
+ from sqlalchemy import within_group
+ stmt = select(
+ department.c.id,
+ func.percentile_cont(0.5).within_group(
+ department.c.salary.desc()
+ )
+ )
+
+ The above statement would produce SQL similar to
+ ``SELECT department.id, percentile_cont(0.5)
+ WITHIN GROUP (ORDER BY department.salary DESC)``.
+
+ :param element: a :class:`.FunctionElement` construct, typically
+ generated by :data:`~.expression.func`.
+ :param \*order_by: one or more column elements that will be used
+ as the ORDER BY clause of the WITHIN GROUP construct.
+
+ .. versionadded:: 1.1
+
+ .. seealso::
+
+ :ref:`tutorial_functions_within_group` - in the
+ :ref:`unified_tutorial`
+
+ :data:`.expression.func`
+
+ :func:`_expression.over`
+
+ """
+ return WithinGroup(element, *order_by)
diff --git a/lib/sqlalchemy/sql/_selectable_constructors.py b/lib/sqlalchemy/sql/_selectable_constructors.py
new file mode 100644
index 000000000..4b67c12f0
--- /dev/null
+++ b/lib/sqlalchemy/sql/_selectable_constructors.py
@@ -0,0 +1,467 @@
+# sql/_selectable_constructors.py
+# Copyright (C) 2005-2022 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
+
+from typing import Any
+from typing import Type
+from typing import Union
+
+from . import coercions
+from . import roles
+from .elements import ColumnClause
+from .selectable import Alias
+from .selectable import CompoundSelect
+from .selectable import Exists
+from .selectable import Join
+from .selectable import Lateral
+from .selectable import Select
+from .selectable import TableClause
+from .selectable import TableSample
+from .selectable import Values
+
+
+def alias(selectable, name=None, flat=False):
+ """Return an :class:`_expression.Alias` object.
+
+ An :class:`_expression.Alias` represents any
+ :class:`_expression.FromClause`
+ with an alternate name assigned within SQL, typically using the ``AS``
+ clause when generated, e.g. ``SELECT * FROM table AS aliasname``.
+
+ Similar functionality is available via the
+ :meth:`_expression.FromClause.alias`
+ method available on all :class:`_expression.FromClause` subclasses.
+ In terms of
+ a SELECT object as generated from the :func:`_expression.select`
+ function, the :meth:`_expression.SelectBase.alias` method returns an
+ :class:`_expression.Alias` or similar object which represents a named,
+ parenthesized subquery.
+
+ When an :class:`_expression.Alias` is created from a
+ :class:`_schema.Table` object,
+ this has the effect of the table being rendered
+ as ``tablename AS aliasname`` in a SELECT statement.
+
+ For :func:`_expression.select` objects, the effect is that of
+ creating a named subquery, i.e. ``(select ...) AS aliasname``.
+
+ The ``name`` parameter is optional, and provides the name
+ to use in the rendered SQL. If blank, an "anonymous" name
+ will be deterministically generated at compile time.
+ Deterministic means the name is guaranteed to be unique against
+ other constructs used in the same statement, and will also be the
+ same name for each successive compilation of the same statement
+ object.
+
+ :param selectable: any :class:`_expression.FromClause` subclass,
+ such as a table, select statement, etc.
+
+ :param name: string name to be assigned as the alias.
+ If ``None``, a name will be deterministically generated
+ at compile time.
+
+ :param flat: Will be passed through to if the given selectable
+ is an instance of :class:`_expression.Join` - see
+ :meth:`_expression.Join.alias`
+ for details.
+
+ """
+ return Alias._factory(selectable, name=name, flat=flat)
+
+
+def cte(selectable, name=None, recursive=False):
+ r"""Return a new :class:`_expression.CTE`,
+ or Common Table Expression instance.
+
+ Please see :meth:`_expression.HasCTE.cte` for detail on CTE usage.
+
+ """
+ return coercions.expect(roles.HasCTERole, selectable).cte(
+ name=name, recursive=recursive
+ )
+
+
+def except_(*selects):
+ r"""Return an ``EXCEPT`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`_expression.CompoundSelect`.
+
+ :param \*selects:
+ a list of :class:`_expression.Select` instances.
+
+ """
+ return CompoundSelect._create_except(*selects)
+
+
+def except_all(*selects):
+ r"""Return an ``EXCEPT ALL`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`_expression.CompoundSelect`.
+
+ :param \*selects:
+ a list of :class:`_expression.Select` instances.
+
+ """
+ return CompoundSelect._create_except_all(*selects)
+
+
+def exists(__argument=None):
+ """Construct a new :class:`_expression.Exists` construct.
+
+ The :func:`_sql.exists` can be invoked by itself to produce an
+ :class:`_sql.Exists` construct, which will accept simple WHERE
+ criteria::
+
+ exists_criteria = exists().where(table1.c.col1 == table2.c.col2)
+
+ However, for greater flexibility in constructing the SELECT, an
+ existing :class:`_sql.Select` construct may be converted to an
+ :class:`_sql.Exists`, most conveniently by making use of the
+ :meth:`_sql.SelectBase.exists` method::
+
+ exists_criteria = (
+ select(table2.c.col2).
+ where(table1.c.col1 == table2.c.col2).
+ exists()
+ )
+
+ The EXISTS criteria is then used inside of an enclosing SELECT::
+
+ stmt = select(table1.c.col1).where(exists_criteria)
+
+ The above statement will then be of the form::
+
+ SELECT col1 FROM table1 WHERE EXISTS
+ (SELECT table2.col2 FROM table2 WHERE table2.col2 = table1.col1)
+
+ .. seealso::
+
+ :ref:`tutorial_exists` - in the :term:`2.0 style` tutorial.
+
+ :meth:`_sql.SelectBase.exists` - method to transform a ``SELECT`` to an
+ ``EXISTS`` clause.
+
+ """ # noqa E501
+
+ return Exists(__argument)
+
+
+def intersect(*selects):
+ r"""Return an ``INTERSECT`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`_expression.CompoundSelect`.
+
+ :param \*selects:
+ a list of :class:`_expression.Select` instances.
+
+ """
+ return CompoundSelect._create_intersect(*selects)
+
+
+def intersect_all(*selects):
+ r"""Return an ``INTERSECT ALL`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`_expression.CompoundSelect`.
+
+ :param \*selects:
+ a list of :class:`_expression.Select` instances.
+
+
+ """
+ return CompoundSelect._create_intersect_all(*selects)
+
+
+def join(left, right, onclause=None, isouter=False, full=False):
+ """Produce a :class:`_expression.Join` object, given two
+ :class:`_expression.FromClause`
+ expressions.
+
+ E.g.::
+
+ j = join(user_table, address_table,
+ user_table.c.id == address_table.c.user_id)
+ stmt = select(user_table).select_from(j)
+
+ would emit SQL along the lines of::
+
+ SELECT user.id, user.name FROM user
+ JOIN address ON user.id = address.user_id
+
+ Similar functionality is available given any
+ :class:`_expression.FromClause` object (e.g. such as a
+ :class:`_schema.Table`) using
+ the :meth:`_expression.FromClause.join` method.
+
+ :param left: The left side of the join.
+
+ :param right: the right side of the join; this is any
+ :class:`_expression.FromClause` object such as a
+ :class:`_schema.Table` object, and
+ may also be a selectable-compatible object such as an ORM-mapped
+ class.
+
+ :param onclause: a SQL expression representing the ON clause of the
+ join. If left at ``None``, :meth:`_expression.FromClause.join`
+ will attempt to
+ join the two tables based on a foreign key relationship.
+
+ :param isouter: if True, render a LEFT OUTER JOIN, instead of JOIN.
+
+ :param full: if True, render a FULL OUTER JOIN, instead of JOIN.
+
+ .. versionadded:: 1.1
+
+ .. seealso::
+
+ :meth:`_expression.FromClause.join` - method form,
+ based on a given left side.
+
+ :class:`_expression.Join` - the type of object produced.
+
+ """
+
+ return Join(left, right, onclause, isouter, full)
+
+
+def lateral(selectable, name=None):
+ """Return a :class:`_expression.Lateral` object.
+
+ :class:`_expression.Lateral` is an :class:`_expression.Alias`
+ subclass that represents
+ a subquery with the LATERAL keyword applied to it.
+
+ The special behavior of a LATERAL subquery is that it appears in the
+ FROM clause of an enclosing SELECT, but may correlate to other
+ FROM clauses of that SELECT. It is a special case of subquery
+ only supported by a small number of backends, currently more recent
+ PostgreSQL versions.
+
+ .. versionadded:: 1.1
+
+ .. seealso::
+
+ :ref:`lateral_selects` - overview of usage.
+
+ """
+ return Lateral._factory(selectable, name=name)
+
+
+def outerjoin(left, right, onclause=None, full=False):
+ """Return an ``OUTER JOIN`` clause element.
+
+ The returned object is an instance of :class:`_expression.Join`.
+
+ Similar functionality is also available via the
+ :meth:`_expression.FromClause.outerjoin` method on any
+ :class:`_expression.FromClause`.
+
+ :param left: The left side of the join.
+
+ :param right: The right side of the join.
+
+ :param onclause: Optional criterion for the ``ON`` clause, is
+ derived from foreign key relationships established between
+ left and right otherwise.
+
+ To chain joins together, use the :meth:`_expression.FromClause.join`
+ or
+ :meth:`_expression.FromClause.outerjoin` methods on the resulting
+ :class:`_expression.Join` object.
+
+ """
+ return Join(left, right, onclause, isouter=True, full=full)
+
+
+def select(*entities: Union[roles.ColumnsClauseRole, Type]) -> "Select":
+ r"""Construct a new :class:`_expression.Select`.
+
+
+ .. versionadded:: 1.4 - The :func:`_sql.select` function now accepts
+ column arguments positionally. The top-level :func:`_sql.select`
+ function will automatically use the 1.x or 2.x style API based on
+ the incoming arguments; using :func:`_future.select` from the
+ ``sqlalchemy.future`` module will enforce that only the 2.x style
+ constructor is used.
+
+ Similar functionality is also available via the
+ :meth:`_expression.FromClause.select` method on any
+ :class:`_expression.FromClause`.
+
+ .. seealso::
+
+ :ref:`coretutorial_selecting` - Core Tutorial description of
+ :func:`_expression.select`.
+
+ :param \*entities:
+ Entities to SELECT from. For Core usage, this is typically a series
+ of :class:`_expression.ColumnElement` and / or
+ :class:`_expression.FromClause`
+ objects which will form the columns clause of the resulting
+ statement. For those objects that are instances of
+ :class:`_expression.FromClause` (typically :class:`_schema.Table`
+ or :class:`_expression.Alias`
+ objects), the :attr:`_expression.FromClause.c`
+ collection is extracted
+ to form a collection of :class:`_expression.ColumnElement` objects.
+
+ This parameter will also accept :class:`_expression.TextClause`
+ constructs as
+ given, as well as ORM-mapped classes.
+
+ """
+
+ return Select(*entities)
+
+
+def table(name: str, *columns: ColumnClause, **kw: Any) -> "TableClause":
+ """Produce a new :class:`_expression.TableClause`.
+
+ The object returned is an instance of
+ :class:`_expression.TableClause`, which
+ represents the "syntactical" portion of the schema-level
+ :class:`_schema.Table` object.
+ It may be used to construct lightweight table constructs.
+
+ .. versionchanged:: 1.0.0 :func:`_expression.table` can now
+ be imported from the plain ``sqlalchemy`` namespace like any
+ other SQL element.
+
+
+ :param name: Name of the table.
+
+ :param columns: A collection of :func:`_expression.column` constructs.
+
+ :param schema: The schema name for this table.
+
+ .. versionadded:: 1.3.18 :func:`_expression.table` can now
+ accept a ``schema`` argument.
+ """
+
+ return TableClause(name, *columns, **kw)
+
+
+def tablesample(selectable, sampling, name=None, seed=None):
+ """Return a :class:`_expression.TableSample` object.
+
+ :class:`_expression.TableSample` is an :class:`_expression.Alias`
+ subclass that represents
+ a table with the TABLESAMPLE clause applied to it.
+ :func:`_expression.tablesample`
+ is also available from the :class:`_expression.FromClause`
+ class via the
+ :meth:`_expression.FromClause.tablesample` method.
+
+ The TABLESAMPLE clause allows selecting a randomly selected approximate
+ percentage of rows from a table. It supports multiple sampling methods,
+ most commonly BERNOULLI and SYSTEM.
+
+ e.g.::
+
+ from sqlalchemy import func
+
+ selectable = people.tablesample(
+ func.bernoulli(1),
+ name='alias',
+ seed=func.random())
+ stmt = select(selectable.c.people_id)
+
+ Assuming ``people`` with a column ``people_id``, the above
+ statement would render as::
+
+ SELECT alias.people_id FROM
+ people AS alias TABLESAMPLE bernoulli(:bernoulli_1)
+ REPEATABLE (random())
+
+ .. versionadded:: 1.1
+
+ :param sampling: a ``float`` percentage between 0 and 100 or
+ :class:`_functions.Function`.
+
+ :param name: optional alias name
+
+ :param seed: any real-valued SQL expression. When specified, the
+ REPEATABLE sub-clause is also rendered.
+
+ """
+ return TableSample._factory(selectable, sampling, name=name, seed=seed)
+
+
+def union(*selects, **kwargs):
+ r"""Return a ``UNION`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`_expression.CompoundSelect`.
+
+ A similar :func:`union()` method is available on all
+ :class:`_expression.FromClause` subclasses.
+
+ :param \*selects:
+ a list of :class:`_expression.Select` instances.
+
+ :param \**kwargs:
+ available keyword arguments are the same as those of
+ :func:`select`.
+
+ """
+ return CompoundSelect._create_union(*selects, **kwargs)
+
+
+def union_all(*selects):
+ r"""Return a ``UNION ALL`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`_expression.CompoundSelect`.
+
+ A similar :func:`union_all()` method is available on all
+ :class:`_expression.FromClause` subclasses.
+
+ :param \*selects:
+ a list of :class:`_expression.Select` instances.
+
+ """
+ return CompoundSelect._create_union_all(*selects)
+
+
+def values(*columns, name=None, literal_binds=False) -> "Values":
+ r"""Construct a :class:`_expression.Values` construct.
+
+ The column expressions and the actual data for
+ :class:`_expression.Values` are given in two separate steps. The
+ constructor receives the column expressions typically as
+ :func:`_expression.column` constructs,
+ and the data is then passed via the
+ :meth:`_expression.Values.data` method as a list,
+ which can be called multiple
+ times to add more data, e.g.::
+
+ from sqlalchemy import column
+ from sqlalchemy import values
+
+ value_expr = values(
+ column('id', Integer),
+ column('name', String),
+ name="my_values"
+ ).data(
+ [(1, 'name1'), (2, 'name2'), (3, 'name3')]
+ )
+
+ :param \*columns: column expressions, typically composed using
+ :func:`_expression.column` objects.
+
+ :param name: the name for this VALUES construct. If omitted, the
+ VALUES construct will be unnamed in a SQL expression. Different
+ backends may have different requirements here.
+
+ :param literal_binds: Defaults to False. Whether or not to render
+ the data values inline in the SQL output, rather than using bound
+ parameters.
+
+ """
+ return Values(*columns, literal_binds=literal_binds, name=name)
diff --git a/lib/sqlalchemy/sql/coercions.py b/lib/sqlalchemy/sql/coercions.py
index fe2b498c8..3bec73f7d 100644
--- a/lib/sqlalchemy/sql/coercions.py
+++ b/lib/sqlalchemy/sql/coercions.py
@@ -8,6 +8,14 @@
import collections.abc as collections_abc
import numbers
import re
+import typing
+from typing import Any
+from typing import Callable
+from typing import Optional
+from typing import overload
+from typing import Type
+from typing import TypeVar
+from typing import Union
from . import operators
from . import roles
@@ -20,13 +28,24 @@ from .. import exc
from .. import inspection
from .. import util
+if not typing.TYPE_CHECKING:
+ elements = None
+ lambdas = None
+ schema = None
+ selectable = None
+ traversals = None
-elements = None
-lambdas = None
-schema = None
-selectable = None
-sqltypes = None
-traversals = None
+if typing.TYPE_CHECKING:
+ from . import elements
+ from . import lambdas
+ from . import schema
+ from . import selectable
+ from . import traversals
+ from .elements import ClauseElement
+ from .elements import ColumnElement
+
+_SR = TypeVar("_SR", bound=roles.SQLRole)
+_StringOnlyR = TypeVar("_StringOnlyR", bound=roles.StringRole)
def _is_literal(element):
@@ -110,14 +129,93 @@ def _expression_collection_was_a_list(attrname, fnname, args):
return args
+@overload
def expect(
- role,
- element,
- apply_propagate_attrs=None,
- argname=None,
- post_inspect=False,
- **kw,
-):
+ role: Type[roles.InElementRole],
+ element: Any,
+ *,
+ apply_propagate_attrs: Optional["ClauseElement"] = None,
+ argname: Optional[str] = None,
+ post_inspect: bool = False,
+ **kw: Any,
+) -> Union["elements.ColumnElement", "selectable.Select"]:
+ ...
+
+
+@overload
+def expect(
+ role: Type[roles.HasCTERole],
+ element: Any,
+ *,
+ apply_propagate_attrs: Optional["ClauseElement"] = None,
+ argname: Optional[str] = None,
+ post_inspect: bool = False,
+ **kw: Any,
+) -> "selectable.HasCTE":
+ ...
+
+
+@overload
+def expect(
+ role: Type[roles.ExpressionElementRole],
+ element: Any,
+ *,
+ apply_propagate_attrs: Optional["ClauseElement"] = None,
+ argname: Optional[str] = None,
+ post_inspect: bool = False,
+ **kw: Any,
+) -> "ColumnElement":
+ ...
+
+
+@overload
+def expect(
+ role: "Type[_StringOnlyR]",
+ element: Any,
+ *,
+ apply_propagate_attrs: Optional["ClauseElement"] = None,
+ argname: Optional[str] = None,
+ post_inspect: bool = False,
+ **kw: Any,
+) -> str:
+ ...
+
+
+@overload
+def expect(
+ role: Type[_SR],
+ element: Any,
+ *,
+ apply_propagate_attrs: Optional["ClauseElement"] = None,
+ argname: Optional[str] = None,
+ post_inspect: bool = False,
+ **kw: Any,
+) -> _SR:
+ ...
+
+
+@overload
+def expect(
+ role: Type[_SR],
+ element: Callable[..., Any],
+ *,
+ apply_propagate_attrs: Optional["ClauseElement"] = None,
+ argname: Optional[str] = None,
+ post_inspect: bool = False,
+ **kw: Any,
+) -> "lambdas.LambdaElement":
+ ...
+
+
+def expect(
+ role: Type[_SR],
+ element: Any,
+ *,
+ apply_propagate_attrs: Optional["ClauseElement"] = None,
+ argname: Optional[str] = None,
+ post_inspect: bool = False,
+ **kw: Any,
+) -> Union[str, _SR, "lambdas.LambdaElement"]:
if (
role.allows_lambda
# note callable() will not invoke a __getattr__() method, whereas
diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py
index 55a586285..1759e686e 100644
--- a/lib/sqlalchemy/sql/default_comparator.py
+++ b/lib/sqlalchemy/sql/default_comparator.py
@@ -8,6 +8,15 @@
"""Default implementation of SQL comparison operations.
"""
+import typing
+from typing import Any
+from typing import Callable
+from typing import Dict
+from typing import NoReturn
+from typing import Optional
+from typing import Tuple
+from typing import Type
+from typing import Union
from . import coercions
from . import operators
@@ -16,28 +25,38 @@ from . import type_api
from .elements import and_
from .elements import BinaryExpression
from .elements import ClauseList
-from .elements import collate
+from .elements import CollationClause
from .elements import CollectionAggregate
from .elements import False_
from .elements import Null
from .elements import or_
from .elements import True_
from .elements import UnaryExpression
+from .operators import OperatorType
from .. import exc
from .. import util
+_T = typing.TypeVar("_T", bound=Any)
+
+if typing.TYPE_CHECKING:
+ from .elements import ColumnElement
+ from .sqltypes import TypeEngine
+
def _boolean_compare(
- expr,
- op,
- obj,
- negate=None,
- reverse=False,
+ expr: "ColumnElement",
+ op: OperatorType,
+ obj: roles.BinaryElementRole,
+ *,
+ negate_op: Optional[OperatorType] = None,
+ reverse: bool = False,
_python_is_types=(util.NoneType, bool),
_any_all_expr=False,
- result_type=None,
- **kwargs,
-):
+ result_type: Optional[
+ Union[Type["TypeEngine[bool]"], "TypeEngine[bool]"]
+ ] = None,
+ **kwargs: Any,
+) -> BinaryExpression[bool]:
if result_type is None:
result_type = type_api.BOOLEANTYPE
@@ -54,7 +73,7 @@ def _boolean_compare(
coercions.expect(roles.ConstExprRole, obj),
op,
type_=result_type,
- negate=negate,
+ negate=negate_op,
modifiers=kwargs,
)
elif op in (
@@ -66,7 +85,7 @@ def _boolean_compare(
coercions.expect(roles.ConstExprRole, obj),
op,
type_=result_type,
- negate=negate,
+ negate=negate_op,
modifiers=kwargs,
)
elif _any_all_expr:
@@ -104,11 +123,21 @@ def _boolean_compare(
if reverse:
return BinaryExpression(
- obj, expr, op, type_=result_type, negate=negate, modifiers=kwargs
+ obj,
+ expr,
+ op,
+ type_=result_type,
+ negate=negate_op,
+ modifiers=kwargs,
)
else:
return BinaryExpression(
- expr, obj, op, type_=result_type, negate=negate, modifiers=kwargs
+ expr,
+ obj,
+ op,
+ type_=result_type,
+ negate=negate_op,
+ modifiers=kwargs,
)
@@ -124,15 +153,26 @@ def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, **kw):
)
-def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw):
- obj = coercions.expect(
+def _binary_operate(
+ expr: "ColumnElement",
+ op: OperatorType,
+ obj: roles.BinaryElementRole,
+ *,
+ reverse=False,
+ result_type: Optional[
+ Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]
+ ] = None,
+ **kw: Any,
+) -> BinaryExpression[_T]:
+
+ coerced_obj = coercions.expect(
roles.BinaryElementRole, obj, expr=expr, operator=op
)
if reverse:
- left, right = obj, expr
+ left, right = coerced_obj, expr
else:
- left, right = expr, obj
+ left, right = expr, coerced_obj
if result_type is None:
op, result_type = left.comparator._adapt_expression(
@@ -142,7 +182,7 @@ def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw):
return BinaryExpression(left, right, op, type_=result_type, modifiers=kw)
-def _conjunction_operate(expr, op, other, **kw):
+def _conjunction_operate(expr, op, other, **kw) -> "ColumnElement":
if op is operators.and_:
return and_(expr, other)
elif op is operators.or_:
@@ -151,11 +191,11 @@ def _conjunction_operate(expr, op, other, **kw):
raise NotImplementedError()
-def _scalar(expr, op, fn, **kw):
+def _scalar(expr, op, fn, **kw) -> "ColumnElement":
return fn(expr)
-def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
+def _in_impl(expr, op, seq_or_selectable, negate_op, **kw) -> "ColumnElement":
seq_or_selectable = coercions.expect(
roles.InElementRole, seq_or_selectable, expr=expr, operator=op
)
@@ -163,11 +203,11 @@ def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
op, negate_op = seq_or_selectable._annotations["in_ops"]
return _boolean_compare(
- expr, op, seq_or_selectable, negate=negate_op, **kw
+ expr, op, seq_or_selectable, negate_op=negate_op, **kw
)
-def _getitem_impl(expr, op, other, **kw):
+def _getitem_impl(expr, op, other, **kw) -> "ColumnElement":
if isinstance(expr.type, type_api.INDEXABLE):
other = coercions.expect(
roles.BinaryElementRole, other, expr=expr, operator=op
@@ -177,13 +217,13 @@ def _getitem_impl(expr, op, other, **kw):
_unsupported_impl(expr, op, other, **kw)
-def _unsupported_impl(expr, op, *arg, **kw):
+def _unsupported_impl(expr, op, *arg, **kw) -> NoReturn:
raise NotImplementedError(
"Operator '%s' is not supported on " "this expression" % op.__name__
)
-def _inv_impl(expr, op, **kw):
+def _inv_impl(expr, op, **kw) -> "ColumnElement":
"""See :meth:`.ColumnOperators.__inv__`."""
# undocumented element currently used by the ORM for
@@ -194,12 +234,12 @@ def _inv_impl(expr, op, **kw):
return expr._negate()
-def _neg_impl(expr, op, **kw):
+def _neg_impl(expr, op, **kw) -> "ColumnElement":
"""See :meth:`.ColumnOperators.__neg__`."""
return UnaryExpression(expr, operator=operators.neg, type_=expr.type)
-def _match_impl(expr, op, other, **kw):
+def _match_impl(expr, op, other, **kw) -> "ColumnElement":
"""See :meth:`.ColumnOperators.match`."""
return _boolean_compare(
@@ -212,21 +252,21 @@ def _match_impl(expr, op, other, **kw):
operator=operators.match_op,
),
result_type=type_api.MATCHTYPE,
- negate=operators.not_match_op
+ negate_op=operators.not_match_op
if op is operators.match_op
else operators.match_op,
**kw,
)
-def _distinct_impl(expr, op, **kw):
+def _distinct_impl(expr, op, **kw) -> "ColumnElement":
"""See :meth:`.ColumnOperators.distinct`."""
return UnaryExpression(
expr, operator=operators.distinct_op, type_=expr.type
)
-def _between_impl(expr, op, cleft, cright, **kw):
+def _between_impl(expr, op, cleft, cright, **kw) -> "ColumnElement":
"""See :meth:`.ColumnOperators.between`."""
return BinaryExpression(
expr,
@@ -255,11 +295,11 @@ def _between_impl(expr, op, cleft, cright, **kw):
)
-def _collate_impl(expr, op, other, **kw):
- return collate(expr, other)
+def _collate_impl(expr, op, collation, **kw) -> "ColumnElement":
+ return CollationClause._create_collation_expression(expr, collation)
-def _regexp_match_impl(expr, op, pattern, flags, **kw):
+def _regexp_match_impl(expr, op, pattern, flags, **kw) -> "ColumnElement":
if flags is not None:
flags = coercions.expect(
roles.BinaryElementRole,
@@ -272,14 +312,16 @@ def _regexp_match_impl(expr, op, pattern, flags, **kw):
op,
pattern,
flags=flags,
- negate=operators.not_regexp_match_op
+ negate_op=operators.not_regexp_match_op
if op is operators.regexp_match_op
else operators.regexp_match_op,
**kw,
)
-def _regexp_replace_impl(expr, op, pattern, replacement, flags, **kw):
+def _regexp_replace_impl(
+ expr, op, pattern, replacement, flags, **kw
+) -> "ColumnElement":
replacement = coercions.expect(
roles.BinaryElementRole,
replacement,
@@ -299,59 +341,118 @@ def _regexp_replace_impl(expr, op, pattern, replacement, flags, **kw):
# a mapping of operators with the method they use, along with
-# their negated operator for comparison operators
-operator_lookup = {
- "and_": (_conjunction_operate,),
- "or_": (_conjunction_operate,),
- "inv": (_inv_impl,),
- "add": (_binary_operate,),
- "mul": (_binary_operate,),
- "sub": (_binary_operate,),
- "div": (_binary_operate,),
- "mod": (_binary_operate,),
- "truediv": (_binary_operate,),
- "floordiv": (_binary_operate,),
- "custom_op": (_custom_op_operate,),
- "json_path_getitem_op": (_binary_operate,),
- "json_getitem_op": (_binary_operate,),
- "concat_op": (_binary_operate,),
- "any_op": (_scalar, CollectionAggregate._create_any),
- "all_op": (_scalar, CollectionAggregate._create_all),
- "lt": (_boolean_compare, operators.ge),
- "le": (_boolean_compare, operators.gt),
- "ne": (_boolean_compare, operators.eq),
- "gt": (_boolean_compare, operators.le),
- "ge": (_boolean_compare, operators.lt),
- "eq": (_boolean_compare, operators.ne),
- "is_distinct_from": (_boolean_compare, operators.is_not_distinct_from),
- "is_not_distinct_from": (_boolean_compare, operators.is_distinct_from),
- "like_op": (_boolean_compare, operators.not_like_op),
- "ilike_op": (_boolean_compare, operators.not_ilike_op),
- "not_like_op": (_boolean_compare, operators.like_op),
- "not_ilike_op": (_boolean_compare, operators.ilike_op),
- "contains_op": (_boolean_compare, operators.not_contains_op),
- "startswith_op": (_boolean_compare, operators.not_startswith_op),
- "endswith_op": (_boolean_compare, operators.not_endswith_op),
- "desc_op": (_scalar, UnaryExpression._create_desc),
- "asc_op": (_scalar, UnaryExpression._create_asc),
- "nulls_first_op": (_scalar, UnaryExpression._create_nulls_first),
- "nulls_last_op": (_scalar, UnaryExpression._create_nulls_last),
- "in_op": (_in_impl, operators.not_in_op),
- "not_in_op": (_in_impl, operators.in_op),
- "is_": (_boolean_compare, operators.is_),
- "is_not": (_boolean_compare, operators.is_not),
- "collate": (_collate_impl,),
- "match_op": (_match_impl,),
- "not_match_op": (_match_impl,),
- "distinct_op": (_distinct_impl,),
- "between_op": (_between_impl,),
- "not_between_op": (_between_impl,),
- "neg": (_neg_impl,),
- "getitem": (_getitem_impl,),
- "lshift": (_unsupported_impl,),
- "rshift": (_unsupported_impl,),
- "contains": (_unsupported_impl,),
- "regexp_match_op": (_regexp_match_impl,),
- "not_regexp_match_op": (_regexp_match_impl,),
- "regexp_replace_op": (_regexp_replace_impl,),
+# additional keyword arguments to be passed
+operator_lookup: Dict[
+ str, Tuple[Callable[..., "ColumnElement"], util.immutabledict]
+] = {
+ "and_": (_conjunction_operate, util.EMPTY_DICT),
+ "or_": (_conjunction_operate, util.EMPTY_DICT),
+ "inv": (_inv_impl, util.EMPTY_DICT),
+ "add": (_binary_operate, util.EMPTY_DICT),
+ "mul": (_binary_operate, util.EMPTY_DICT),
+ "sub": (_binary_operate, util.EMPTY_DICT),
+ "div": (_binary_operate, util.EMPTY_DICT),
+ "mod": (_binary_operate, util.EMPTY_DICT),
+ "truediv": (_binary_operate, util.EMPTY_DICT),
+ "floordiv": (_binary_operate, util.EMPTY_DICT),
+ "custom_op": (_custom_op_operate, util.EMPTY_DICT),
+ "json_path_getitem_op": (_binary_operate, util.EMPTY_DICT),
+ "json_getitem_op": (_binary_operate, util.EMPTY_DICT),
+ "concat_op": (_binary_operate, util.EMPTY_DICT),
+ "any_op": (
+ _scalar,
+ util.immutabledict({"fn": CollectionAggregate._create_any}),
+ ),
+ "all_op": (
+ _scalar,
+ util.immutabledict({"fn": CollectionAggregate._create_all}),
+ ),
+ "lt": (_boolean_compare, util.immutabledict({"negate_op": operators.ge})),
+ "le": (_boolean_compare, util.immutabledict({"negate_op": operators.gt})),
+ "ne": (_boolean_compare, util.immutabledict({"negate_op": operators.eq})),
+ "gt": (_boolean_compare, util.immutabledict({"negate_op": operators.le})),
+ "ge": (_boolean_compare, util.immutabledict({"negate_op": operators.lt})),
+ "eq": (_boolean_compare, util.immutabledict({"negate_op": operators.ne})),
+ "is_distinct_from": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.is_not_distinct_from}),
+ ),
+ "is_not_distinct_from": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.is_distinct_from}),
+ ),
+ "like_op": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.not_like_op}),
+ ),
+ "ilike_op": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.not_ilike_op}),
+ ),
+ "not_like_op": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.like_op}),
+ ),
+ "not_ilike_op": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.ilike_op}),
+ ),
+ "contains_op": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.not_contains_op}),
+ ),
+ "startswith_op": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.not_startswith_op}),
+ ),
+ "endswith_op": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.not_endswith_op}),
+ ),
+ "desc_op": (
+ _scalar,
+ util.immutabledict({"fn": UnaryExpression._create_desc}),
+ ),
+ "asc_op": (
+ _scalar,
+ util.immutabledict({"fn": UnaryExpression._create_asc}),
+ ),
+ "nulls_first_op": (
+ _scalar,
+ util.immutabledict({"fn": UnaryExpression._create_nulls_first}),
+ ),
+ "nulls_last_op": (
+ _scalar,
+ util.immutabledict({"fn": UnaryExpression._create_nulls_last}),
+ ),
+ "in_op": (
+ _in_impl,
+ util.immutabledict({"negate_op": operators.not_in_op}),
+ ),
+ "not_in_op": (
+ _in_impl,
+ util.immutabledict({"negate_op": operators.in_op}),
+ ),
+ "is_": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.is_}),
+ ),
+ "is_not": (
+ _boolean_compare,
+ util.immutabledict({"negate_op": operators.is_not}),
+ ),
+ "collate": (_collate_impl, util.EMPTY_DICT),
+ "match_op": (_match_impl, util.EMPTY_DICT),
+ "not_match_op": (_match_impl, util.EMPTY_DICT),
+ "distinct_op": (_distinct_impl, util.EMPTY_DICT),
+ "between_op": (_between_impl, util.EMPTY_DICT),
+ "not_between_op": (_between_impl, util.EMPTY_DICT),
+ "neg": (_neg_impl, util.EMPTY_DICT),
+ "getitem": (_getitem_impl, util.EMPTY_DICT),
+ "lshift": (_unsupported_impl, util.EMPTY_DICT),
+ "rshift": (_unsupported_impl, util.EMPTY_DICT),
+ "contains": (_unsupported_impl, util.EMPTY_DICT),
+ "regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT),
+ "not_regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT),
+ "regexp_replace_op": (_regexp_replace_impl, util.EMPTY_DICT),
}
diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py
index 5f2424466..33dca66cd 100644
--- a/lib/sqlalchemy/sql/dml.py
+++ b/lib/sqlalchemy/sql/dml.py
@@ -763,76 +763,7 @@ class Insert(ValuesBase):
+ HasCTE._has_ctes_traverse_internals
)
- def __init__(
- self,
- table,
- ):
- """Construct an :class:`_expression.Insert` object.
-
- E.g.::
-
- from sqlalchemy import insert
-
- stmt = (
- insert(user_table).
- values(name='username', fullname='Full Username')
- )
-
- Similar functionality is available via the
- :meth:`_expression.TableClause.insert` method on
- :class:`_schema.Table`.
-
- .. seealso::
-
- :ref:`coretutorial_insert_expressions` - in the
- :ref:`1.x tutorial <sqlexpression_toplevel>`
-
- :ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
-
-
- :param table: :class:`_expression.TableClause`
- which is the subject of the
- insert.
-
- :param values: collection of values to be inserted; see
- :meth:`_expression.Insert.values`
- for a description of allowed formats here.
- Can be omitted entirely; a :class:`_expression.Insert` construct
- will also dynamically render the VALUES clause at execution time
- based on the parameters passed to :meth:`_engine.Connection.execute`.
-
- :param inline: if True, no attempt will be made to retrieve the
- SQL-generated default values to be provided within the statement;
- in particular,
- this allows SQL expressions to be rendered 'inline' within the
- statement without the need to pre-execute them beforehand; for
- backends that support "returning", this turns off the "implicit
- returning" feature for the statement.
-
- If both :paramref:`_expression.Insert.values` and compile-time bind
- parameters are present, the compile-time bind parameters override the
- information specified within :paramref:`_expression.Insert.values` on a
- per-key basis.
-
- The keys within :paramref:`_expression.Insert.values` can be either
- :class:`~sqlalchemy.schema.Column` objects or their string
- identifiers. Each key may reference one of:
-
- * a literal data value (i.e. string, number, etc.);
- * a Column object;
- * a SELECT statement.
-
- If a ``SELECT`` statement is specified which references this
- ``INSERT`` statement's table, the statement will be correlated
- against the ``INSERT`` statement.
-
- .. seealso::
-
- :ref:`coretutorial_insert_expressions` - SQL Expression Tutorial
-
- :ref:`inserts_and_updates` - SQL Expression Tutorial
-
- """
+ def __init__(self, table):
super(Insert, self).__init__(table)
@_generative
@@ -1045,118 +976,7 @@ class Update(DMLWhereBase, ValuesBase):
+ HasCTE._has_ctes_traverse_internals
)
- def __init__(
- self,
- table,
- ):
- r"""Construct an :class:`_expression.Update` object.
-
- E.g.::
-
- from sqlalchemy import update
-
- stmt = (
- update(user_table).
- where(user_table.c.id == 5).
- values(name='user #5')
- )
-
- Similar functionality is available via the
- :meth:`_expression.TableClause.update` method on
- :class:`_schema.Table`.
-
- .. seealso::
-
- :ref:`inserts_and_updates` - in the
- :ref:`1.x tutorial <sqlexpression_toplevel>`
-
- :ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
-
-
-
- :param table: A :class:`_schema.Table`
- object representing the database
- table to be updated.
-
- :param whereclause: Optional SQL expression describing the ``WHERE``
- condition of the ``UPDATE`` statement; is equivalent to using the
- more modern :meth:`~Update.where()` method to specify the ``WHERE``
- clause.
-
- :param values:
- Optional dictionary which specifies the ``SET`` conditions of the
- ``UPDATE``. If left as ``None``, the ``SET``
- conditions are determined from those parameters passed to the
- statement during the execution and/or compilation of the
- statement. When compiled standalone without any parameters,
- the ``SET`` clause generates for all columns.
-
- Modern applications may prefer to use the generative
- :meth:`_expression.Update.values` method to set the values of the
- UPDATE statement.
-
- :param inline:
- if True, SQL defaults present on :class:`_schema.Column` objects via
- the ``default`` keyword will be compiled 'inline' into the statement
- and not pre-executed. This means that their values will not
- be available in the dictionary returned from
- :meth:`_engine.CursorResult.last_updated_params`.
-
- :param preserve_parameter_order: if True, the update statement is
- expected to receive parameters **only** via the
- :meth:`_expression.Update.values` method,
- and they must be passed as a Python
- ``list`` of 2-tuples. The rendered UPDATE statement will emit the SET
- clause for each referenced column maintaining this order.
-
- .. versionadded:: 1.0.10
-
- .. seealso::
-
- :ref:`updates_order_parameters` - illustrates the
- :meth:`_expression.Update.ordered_values` method.
-
- If both ``values`` and compile-time bind parameters are present, the
- compile-time bind parameters override the information specified
- within ``values`` on a per-key basis.
-
- The keys within ``values`` can be either :class:`_schema.Column`
- objects or their string identifiers (specifically the "key" of the
- :class:`_schema.Column`, normally but not necessarily equivalent to
- its "name"). Normally, the
- :class:`_schema.Column` objects used here are expected to be
- part of the target :class:`_schema.Table` that is the table
- to be updated. However when using MySQL, a multiple-table
- UPDATE statement can refer to columns from any of
- the tables referred to in the WHERE clause.
-
- The values referred to in ``values`` are typically:
-
- * a literal data value (i.e. string, number, etc.)
- * a SQL expression, such as a related :class:`_schema.Column`,
- a scalar-returning :func:`_expression.select` construct,
- etc.
-
- When combining :func:`_expression.select` constructs within the
- values clause of an :func:`_expression.update`
- construct, the subquery represented
- by the :func:`_expression.select` should be *correlated* to the
- parent table, that is, providing criterion which links the table inside
- the subquery to the outer table being updated::
-
- users.update().values(
- name=select(addresses.c.email_address).\
- where(addresses.c.user_id==users.c.id).\
- scalar_subquery()
- )
-
- .. seealso::
-
- :ref:`inserts_and_updates` - SQL Expression
- Language Tutorial
-
-
- """
+ def __init__(self, table):
super(Update, self).__init__(table)
@_generative
@@ -1244,45 +1064,7 @@ class Delete(DMLWhereBase, UpdateBase):
+ HasCTE._has_ctes_traverse_internals
)
- def __init__(
- self,
- table,
- ):
- r"""Construct :class:`_expression.Delete` object.
-
- E.g.::
-
- from sqlalchemy import delete
-
- stmt = (
- delete(user_table).
- where(user_table.c.id == 5)
- )
-
- Similar functionality is available via the
- :meth:`_expression.TableClause.delete` method on
- :class:`_schema.Table`.
-
- .. seealso::
-
- :ref:`inserts_and_updates` - in the
- :ref:`1.x tutorial <sqlexpression_toplevel>`
-
- :ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
-
-
- :param table: The table to delete rows from.
-
- :param whereclause: Optional SQL expression describing the ``WHERE``
- condition of the ``DELETE`` statement; is equivalent to using the
- more modern :meth:`~Delete.where()` method to specify the ``WHERE``
- clause.
-
- .. seealso::
-
- :ref:`deletes` - SQL Expression Tutorial
-
- """
+ def __init__(self, table):
self.table = coercions.expect(
roles.DMLTableRole, table, apply_propagate_attrs=self
)
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index a025cce35..705a89889 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -14,6 +14,16 @@ import itertools
import operator
import re
import typing
+from typing import Any
+from typing import Callable
+from typing import Generic
+from typing import Optional
+from typing import overload
+from typing import Sequence
+from typing import Text as typing_Text
+from typing import Type
+from typing import TypeVar
+from typing import Union
from . import coercions
from . import operators
@@ -31,7 +41,7 @@ from .base import NO_ARG
from .base import SingletonConstant
from .cache_key import MemoizedHasCacheKey
from .cache_key import NO_CACHE
-from .coercions import _document_text_coercion
+from .coercions import _document_text_coercion # noqa
from .traversals import HasCopyInternals
from .visitors import cloned_traverse
from .visitors import InternalTraversal
@@ -41,86 +51,20 @@ from .. import exc
from .. import inspection
from .. import util
+if typing.TYPE_CHECKING:
+ from decimal import Decimal
-def collate(expression, collation):
- """Return the clause ``expression COLLATE collation``.
+ from .selectable import FromClause
+ from .selectable import Select
+ from .sqltypes import Boolean # noqa
+ from .type_api import TypeEngine
- e.g.::
+_NUMERIC = Union[complex, "Decimal"]
- collate(mycolumn, 'utf8_bin')
-
- produces::
-
- mycolumn COLLATE utf8_bin
-
- The collation expression is also quoted if it is a case sensitive
- identifier, e.g. contains uppercase characters.
-
- .. versionchanged:: 1.2 quoting is automatically applied to COLLATE
- expressions if they are case sensitive.
-
- """
-
- expr = coercions.expect(roles.ExpressionElementRole, expression)
- return BinaryExpression(
- expr, CollationClause(collation), operators.collate, type_=expr.type
- )
-
-
-def between(expr, lower_bound, upper_bound, symmetric=False):
- """Produce a ``BETWEEN`` predicate clause.
-
- E.g.::
-
- from sqlalchemy import between
- stmt = select(users_table).where(between(users_table.c.id, 5, 7))
-
- Would produce SQL resembling::
-
- SELECT id, name FROM user WHERE id BETWEEN :id_1 AND :id_2
-
- The :func:`.between` function is a standalone version of the
- :meth:`_expression.ColumnElement.between` method available on all
- SQL expressions, as in::
-
- stmt = select(users_table).where(users_table.c.id.between(5, 7))
-
- All arguments passed to :func:`.between`, including the left side
- column expression, are coerced from Python scalar values if a
- the value is not a :class:`_expression.ColumnElement` subclass.
- For example,
- three fixed values can be compared as in::
-
- print(between(5, 3, 7))
-
- Which would produce::
-
- :param_1 BETWEEN :param_2 AND :param_3
-
- :param expr: a column expression, typically a
- :class:`_expression.ColumnElement`
- instance or alternatively a Python scalar expression to be coerced
- into a column expression, serving as the left side of the ``BETWEEN``
- expression.
-
- :param lower_bound: a column or Python scalar expression serving as the
- lower bound of the right side of the ``BETWEEN`` expression.
-
- :param upper_bound: a column or Python scalar expression serving as the
- upper bound of the right side of the ``BETWEEN`` expression.
-
- :param symmetric: if True, will render " BETWEEN SYMMETRIC ". Note
- that not all databases support this syntax.
-
- .. versionadded:: 0.9.5
-
- .. seealso::
-
- :meth:`_expression.ColumnElement.between`
-
- """
- expr = coercions.expect(roles.ExpressionElementRole, expr)
- return expr.between(lower_bound, upper_bound, symmetric=symmetric)
+_T = TypeVar("_T", bound="Any")
+_OPT = TypeVar("_OPT", bound="Any")
+_NT = TypeVar("_NT", bound="_NUMERIC")
+_ST = TypeVar("_ST", bound="typing_Text")
def literal(value, type_=None):
@@ -145,28 +89,40 @@ def literal(value, type_=None):
return coercions.expect(roles.LiteralValueRole, value, type_=type_)
-def outparam(key, type_=None):
- """Create an 'OUT' parameter for usage in functions (stored procedures),
- for databases which support them.
+def literal_column(text, type_=None):
+ r"""Produce a :class:`.ColumnClause` object that has the
+ :paramref:`_expression.column.is_literal` flag set to True.
+
+ :func:`_expression.literal_column` is similar to
+ :func:`_expression.column`, except that
+ it is more often used as a "standalone" column expression that renders
+ exactly as stated; while :func:`_expression.column`
+ stores a string name that
+ will be assumed to be part of a table and may be quoted as such,
+ :func:`_expression.literal_column` can be that,
+ or any other arbitrary column-oriented
+ expression.
+
+ :param text: the text of the expression; can be any SQL expression.
+ Quoting rules will not be applied. To specify a column-name expression
+ which should be subject to quoting rules, use the :func:`column`
+ function.
- The ``outparam`` can be used like a regular function parameter.
- The "output" value will be available from the
- :class:`~sqlalchemy.engine.CursorResult` object via its ``out_parameters``
- attribute, which returns a dictionary containing the values.
+ :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine`
+ object which will
+ provide result-set translation and additional expression semantics for
+ this column. If left as ``None`` the type will be :class:`.NullType`.
- """
- return BindParameter(key, None, type_=type_, unique=False, isoutparam=True)
+ .. seealso::
+ :func:`_expression.column`
-def not_(clause):
- """Return a negation of the given clause, i.e. ``NOT(clause)``.
+ :func:`_expression.text`
- The ``~`` operator is also overloaded on all
- :class:`_expression.ColumnElement` subclasses to produce the
- same result.
+ :ref:`sqlexpression_literal_column`
"""
- return operators.inv(coercions.expect(roles.ExpressionElementRole, clause))
+ return ColumnClause(text, type_=type_, is_literal=True)
class CompilerElement(Traversible):
@@ -258,6 +214,9 @@ class CompilerElement(Traversible):
return str(self.compile())
+SelfClauseElement = TypeVar("SelfClauseElement", bound="ClauseElement")
+
+
@inspection._self_inspects
class ClauseElement(
SupportsWrappingAnnotations,
@@ -313,7 +272,7 @@ class ClauseElement(
self._propagate_attrs = util.immutabledict(values)
return self
- def _clone(self, **kw):
+ def _clone(self: SelfClauseElement, **kw) -> SelfClauseElement:
"""Create a shallow copy of this ClauseElement.
This method may be used by a generative API. Its also used as
@@ -624,8 +583,9 @@ class ColumnElement(
roles.DMLColumnRole,
roles.DDLConstraintColumnRole,
roles.DDLExpressionRole,
- operators.ColumnOperators,
+ operators.ColumnOperators["ColumnElement"],
ClauseElement,
+ Generic[_T],
):
"""Represent a column-oriented SQL expression suitable for usage in the
"columns" clause, WHERE clause etc. of a statement.
@@ -841,11 +801,11 @@ class ColumnElement(
return super(ColumnElement, self)._negate()
@util.memoized_property
- def type(self):
+ def type(self) -> "TypeEngine[_T]":
return type_api.NULLTYPE
@HasMemoized.memoized_attribute
- def comparator(self):
+ def comparator(self) -> "TypeEngine.Comparator[_T]":
try:
comparator_factory = self.type.comparator_factory
except AttributeError as err:
@@ -869,10 +829,347 @@ class ColumnElement(
)
) from err
- def operate(self, op, *other, **kwargs):
+ # annotations for comparison methods
+ # these are from operators->Operators / ColumnOperators,
+ # redefined with the specific types returned by ColumnElement hierarchies
+ if typing.TYPE_CHECKING:
+
+ def op(
+ self,
+ opstring: Any,
+ precedence: int = 0,
+ is_comparison: bool = False,
+ return_type: Optional[
+ Union[Type["TypeEngine[_OPT]"], "TypeEngine[_OPT]"]
+ ] = None,
+ python_impl=None,
+ ) -> Callable[[Any], "BinaryExpression[_OPT]"]:
+ ...
+
+ def bool_op(
+ self, opstring: Any, precedence: int = 0, python_impl=None
+ ) -> Callable[[Any], "BinaryExpression[bool]"]:
+ ...
+
+ def __and__(self, other: Any) -> "BooleanClauseList":
+ ...
+
+ def __or__(self, other: Any) -> "BooleanClauseList":
+ ...
+
+ def __invert__(self) -> "UnaryExpression[_T]":
+ ...
+
+ def __lt__(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def __le__(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def __eq__(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def __ne__(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def is_distinct_from(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def is_not_distinct_from(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def __gt__(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def __ge__(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def __neg__(self) -> "UnaryExpression[_T]":
+ ...
+
+ def __contains__(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def __getitem__(self, index: Any) -> "ColumnElement":
+ ...
+
+ @overload
+ def concat(self, other: Any) -> "BinaryExpression[_ST]":
+ ...
+
+ @overload
+ def concat(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def concat(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def like(self, other: Any, escape=None) -> "BinaryExpression[bool]":
+ ...
+
+ def ilike(self, other: Any, escape=None) -> "BinaryExpression[bool]":
+ ...
+
+ def in_(
+ self,
+ other: Union[Sequence[Any], "BindParameter", "Select"],
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def not_in(
+ self,
+ other: Union[Sequence[Any], "BindParameter", "Select"],
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def not_like(
+ self, other: Any, escape=None
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def not_ilike(
+ self, other: Any, escape=None
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def is_(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def is_not(self, other: Any) -> "BinaryExpression[bool]":
+ ...
+
+ def startswith(
+ self, other: Any, escape=None, autoescape=False
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def endswith(
+ self, other: Any, escape=None, autoescape=False
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def contains(
+ self, other: Any, escape=None, autoescape=False
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def match(self, other: Any, **kwargs) -> "BinaryExpression[bool]":
+ ...
+
+ def regexp_match(
+ self, pattern, flags=None
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def regexp_replace(
+ self, pattern, replacement, flags=None
+ ) -> "BinaryExpression":
+ ...
+
+ def desc(self) -> "UnaryExpression[_T]":
+ ...
+
+ def asc(self) -> "UnaryExpression[_T]":
+ ...
+
+ def nulls_first(self) -> "UnaryExpression[_T]":
+ ...
+
+ def nulls_last(self) -> "UnaryExpression[_T]":
+ ...
+
+ def collate(self, collation) -> "CollationClause":
+ ...
+
+ def between(
+ self, cleft, cright, symmetric=False
+ ) -> "BinaryExpression[bool]":
+ ...
+
+ def distinct(self: "ColumnElement[_T]") -> "UnaryExpression[_T]":
+ ...
+
+ def any_(self) -> "CollectionAggregate":
+ ...
+
+ def all_(self) -> "CollectionAggregate":
+ ...
+
+ # numeric overloads. These need more tweaking
+
+ @overload
+ def __add__(
+ self: "ColumnElement[_NT]", other: "Union[ColumnElement[_NT], _NT]"
+ ) -> "BinaryExpression[_NT]":
+ ...
+
+ @overload
+ def __add__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __add__(
+ self: "ColumnElement[_ST]", other: Any
+ ) -> "BinaryExpression[_ST]":
+ ...
+
+ def __add__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __radd__(self, other: Any) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __radd__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __radd__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __sub__(
+ self: "ColumnElement[_NT]", other: "Union[ColumnElement[_NT], _NT]"
+ ) -> "BinaryExpression[_NT]":
+ ...
+
+ @overload
+ def __sub__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __sub__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __rsub__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __rsub__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __rsub__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __mul__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __mul__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __mul__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __rmul__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __rmul__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __rmul__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __mod__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __mod__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __mod__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __rmod__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __rmod__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __rmod__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __truediv__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __truediv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __truediv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __rtruediv__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __rtruediv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __rtruediv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __floordiv__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __floordiv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __floordiv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ @overload
+ def __rfloordiv__(
+ self: "ColumnElement[_NT]", other: Any
+ ) -> "BinaryExpression[_NUMERIC]":
+ ...
+
+ @overload
+ def __rfloordiv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def __rfloordiv__(self, other: Any) -> "BinaryExpression":
+ ...
+
+ def operate(
+ self,
+ op: operators.OperatorType,
+ *other: Any,
+ **kwargs,
+ ) -> "ColumnElement":
return op(self.comparator, *other, **kwargs)
- def reverse_operate(self, op, other, **kwargs):
+ def reverse_operate(
+ self, op: operators.OperatorType, other: Any, **kwargs
+ ) -> "ColumnElement":
return op(other, self.comparator, **kwargs)
def _bind_param(self, operator, obj, type_=None, expanding=False):
@@ -975,7 +1272,12 @@ class ColumnElement(
return None
def _make_proxy(
- self, selectable, name=None, key=None, name_is_truncatable=False, **kw
+ self,
+ selectable,
+ name: Optional[str] = None,
+ key=None,
+ name_is_truncatable=False,
+ **kw,
):
"""Create a new :class:`_expression.ColumnElement` representing this
:class:`_expression.ColumnElement` as it appears in the select list of
@@ -1031,7 +1333,7 @@ class ColumnElement(
"""
return Label(name, self, self.type)
- def _anon_label(self, seed, add_hash=None):
+ def _anon_label(self, seed, add_hash=None) -> "_anonymous_label":
while self._is_clone_of is not None:
self = self._is_clone_of
@@ -1066,7 +1368,7 @@ class ColumnElement(
return _anonymous_label.safe_construct(hash_value, seed or "anon")
@util.memoized_property
- def _anon_name_label(self):
+ def _anon_name_label(self) -> "_anonymous_label":
"""Provides a constant 'anonymous label' for this ColumnElement.
This is a label() expression which will be named at compile time.
@@ -1214,7 +1516,10 @@ class WrapsColumnExpression:
return self._dedupe_anon_tq_label_idx(idx)
-class BindParameter(roles.InElementRole, ColumnElement):
+SelfBindParameter = TypeVar("SelfBindParameter", bound="BindParameter")
+
+
+class BindParameter(roles.InElementRole, ColumnElement[_T]):
r"""Represent a "bound expression".
:class:`.BindParameter` is invoked explicitly using the
@@ -1267,238 +1572,6 @@ class BindParameter(roles.InElementRole, ColumnElement):
_compared_to_type=None,
_is_crud=False,
):
- r"""Produce a "bound expression".
-
- The return value is an instance of :class:`.BindParameter`; this
- is a :class:`_expression.ColumnElement`
- subclass which represents a so-called
- "placeholder" value in a SQL expression, the value of which is
- supplied at the point at which the statement in executed against a
- database connection.
-
- In SQLAlchemy, the :func:`.bindparam` construct has
- the ability to carry along the actual value that will be ultimately
- used at expression time. In this way, it serves not just as
- a "placeholder" for eventual population, but also as a means of
- representing so-called "unsafe" values which should not be rendered
- directly in a SQL statement, but rather should be passed along
- to the :term:`DBAPI` as values which need to be correctly escaped
- and potentially handled for type-safety.
-
- When using :func:`.bindparam` explicitly, the use case is typically
- one of traditional deferment of parameters; the :func:`.bindparam`
- construct accepts a name which can then be referred to at execution
- time::
-
- from sqlalchemy import bindparam
-
- stmt = select(users_table).\
- where(users_table.c.name == bindparam('username'))
-
- The above statement, when rendered, will produce SQL similar to::
-
- SELECT id, name FROM user WHERE name = :username
-
- In order to populate the value of ``:username`` above, the value
- would typically be applied at execution time to a method
- like :meth:`_engine.Connection.execute`::
-
- result = connection.execute(stmt, username='wendy')
-
- Explicit use of :func:`.bindparam` is also common when producing
- UPDATE or DELETE statements that are to be invoked multiple times,
- where the WHERE criterion of the statement is to change on each
- invocation, such as::
-
- stmt = (users_table.update().
- where(user_table.c.name == bindparam('username')).
- values(fullname=bindparam('fullname'))
- )
-
- connection.execute(
- stmt, [{"username": "wendy", "fullname": "Wendy Smith"},
- {"username": "jack", "fullname": "Jack Jones"},
- ]
- )
-
- SQLAlchemy's Core expression system makes wide use of
- :func:`.bindparam` in an implicit sense. It is typical that Python
- literal values passed to virtually all SQL expression functions are
- coerced into fixed :func:`.bindparam` constructs. For example, given
- a comparison operation such as::
-
- expr = users_table.c.name == 'Wendy'
-
- The above expression will produce a :class:`.BinaryExpression`
- construct, where the left side is the :class:`_schema.Column` object
- representing the ``name`` column, and the right side is a
- :class:`.BindParameter` representing the literal value::
-
- print(repr(expr.right))
- BindParameter('%(4327771088 name)s', 'Wendy', type_=String())
-
- The expression above will render SQL such as::
-
- user.name = :name_1
-
- Where the ``:name_1`` parameter name is an anonymous name. The
- actual string ``Wendy`` is not in the rendered string, but is carried
- along where it is later used within statement execution. If we
- invoke a statement like the following::
-
- stmt = select(users_table).where(users_table.c.name == 'Wendy')
- result = connection.execute(stmt)
-
- We would see SQL logging output as::
-
- SELECT "user".id, "user".name
- FROM "user"
- WHERE "user".name = %(name_1)s
- {'name_1': 'Wendy'}
-
- Above, we see that ``Wendy`` is passed as a parameter to the database,
- while the placeholder ``:name_1`` is rendered in the appropriate form
- for the target database, in this case the PostgreSQL database.
-
- Similarly, :func:`.bindparam` is invoked automatically when working
- with :term:`CRUD` statements as far as the "VALUES" portion is
- concerned. The :func:`_expression.insert` construct produces an
- ``INSERT`` expression which will, at statement execution time, generate
- bound placeholders based on the arguments passed, as in::
-
- stmt = users_table.insert()
- result = connection.execute(stmt, name='Wendy')
-
- The above will produce SQL output as::
-
- INSERT INTO "user" (name) VALUES (%(name)s)
- {'name': 'Wendy'}
-
- The :class:`_expression.Insert` construct, at
- compilation/execution time, rendered a single :func:`.bindparam`
- mirroring the column name ``name`` as a result of the single ``name``
- parameter we passed to the :meth:`_engine.Connection.execute` method.
-
- :param key:
- the key (e.g. the name) for this bind param.
- Will be used in the generated
- SQL statement for dialects that use named parameters. This
- value may be modified when part of a compilation operation,
- if other :class:`BindParameter` objects exist with the same
- key, or if its length is too long and truncation is
- required.
-
- :param value:
- Initial value for this bind param. Will be used at statement
- execution time as the value for this parameter passed to the
- DBAPI, if no other value is indicated to the statement execution
- method for this particular parameter name. Defaults to ``None``.
-
- :param callable\_:
- A callable function that takes the place of "value". The function
- will be called at statement execution time to determine the
- ultimate value. Used for scenarios where the actual bind
- value cannot be determined at the point at which the clause
- construct is created, but embedded bind values are still desirable.
-
- :param type\_:
- A :class:`.TypeEngine` class or instance representing an optional
- datatype for this :func:`.bindparam`. If not passed, a type
- may be determined automatically for the bind, based on the given
- value; for example, trivial Python types such as ``str``,
- ``int``, ``bool``
- may result in the :class:`.String`, :class:`.Integer` or
- :class:`.Boolean` types being automatically selected.
-
- The type of a :func:`.bindparam` is significant especially in that
- the type will apply pre-processing to the value before it is
- passed to the database. For example, a :func:`.bindparam` which
- refers to a datetime value, and is specified as holding the
- :class:`.DateTime` type, may apply conversion needed to the
- value (such as stringification on SQLite) before passing the value
- to the database.
-
- :param unique:
- if True, the key name of this :class:`.BindParameter` will be
- modified if another :class:`.BindParameter` of the same name
- already has been located within the containing
- expression. This flag is used generally by the internals
- when producing so-called "anonymous" bound expressions, it
- isn't generally applicable to explicitly-named :func:`.bindparam`
- constructs.
-
- :param required:
- If ``True``, a value is required at execution time. If not passed,
- it defaults to ``True`` if neither :paramref:`.bindparam.value`
- or :paramref:`.bindparam.callable` were passed. If either of these
- parameters are present, then :paramref:`.bindparam.required`
- defaults to ``False``.
-
- :param quote:
- True if this parameter name requires quoting and is not
- currently known as a SQLAlchemy reserved word; this currently
- only applies to the Oracle backend, where bound names must
- sometimes be quoted.
-
- :param isoutparam:
- if True, the parameter should be treated like a stored procedure
- "OUT" parameter. This applies to backends such as Oracle which
- support OUT parameters.
-
- :param expanding:
- if True, this parameter will be treated as an "expanding" parameter
- at execution time; the parameter value is expected to be a sequence,
- rather than a scalar value, and the string SQL statement will
- be transformed on a per-execution basis to accommodate the sequence
- with a variable number of parameter slots passed to the DBAPI.
- This is to allow statement caching to be used in conjunction with
- an IN clause.
-
- .. seealso::
-
- :meth:`.ColumnOperators.in_`
-
- :ref:`baked_in` - with baked queries
-
- .. note:: The "expanding" feature does not support "executemany"-
- style parameter sets.
-
- .. versionadded:: 1.2
-
- .. versionchanged:: 1.3 the "expanding" bound parameter feature now
- supports empty lists.
-
-
- .. seealso::
-
- :ref:`coretutorial_bind_param`
-
- :ref:`coretutorial_insert_expressions`
-
- :func:`.outparam`
-
- :param literal_execute:
- if True, the bound parameter will be rendered in the compile phase
- with a special "POSTCOMPILE" token, and the SQLAlchemy compiler will
- render the final value of the parameter into the SQL statement at
- statement execution time, omitting the value from the parameter
- dictionary / list passed to DBAPI ``cursor.execute()``. This
- produces a similar effect as that of using the ``literal_binds``,
- compilation flag, however takes place as the statement is sent to
- the DBAPI ``cursor.execute()`` method, rather than when the statement
- is compiled. The primary use of this
- capability is for rendering LIMIT / OFFSET clauses for database
- drivers that can't accommodate for bound parameters in these
- contexts, while allowing SQL constructs to be cacheable at the
- compilation level.
-
- .. versionadded:: 1.4 Added "post compile" bound parameters
-
- .. seealso::
-
- :ref:`change_4808`.
-
- """
if required is NO_ARG:
required = value is NO_ARG and callable_ is None
if value is NO_ARG:
@@ -1641,7 +1714,9 @@ class BindParameter(roles.InElementRole, ColumnElement):
c.type = type_
return c
- def _clone(self, maintain_key=False, **kw):
+ def _clone(
+ self: SelfBindParameter, maintain_key=False, **kw
+ ) -> SelfBindParameter:
c = ClauseElement._clone(self, **kw)
if not maintain_key and self.unique:
c.key = _anonymous_label.safe_construct(
@@ -1799,85 +1874,6 @@ class TextClause(
# to the list of bindparams
self.text = self._bind_params_regex.sub(repl, text)
- @classmethod
- @_document_text_coercion("text", ":func:`.text`", ":paramref:`.text.text`")
- def _create_text(cls, text):
- r"""Construct a new :class:`_expression.TextClause` clause,
- representing
- a textual SQL string directly.
-
- E.g.::
-
- from sqlalchemy import text
-
- t = text("SELECT * FROM users")
- result = connection.execute(t)
-
- The advantages :func:`_expression.text`
- provides over a plain string are
- backend-neutral support for bind parameters, per-statement
- execution options, as well as
- bind parameter and result-column typing behavior, allowing
- SQLAlchemy type constructs to play a role when executing
- a statement that is specified literally. The construct can also
- be provided with a ``.c`` collection of column elements, allowing
- it to be embedded in other SQL expression constructs as a subquery.
-
- Bind parameters are specified by name, using the format ``:name``.
- E.g.::
-
- t = text("SELECT * FROM users WHERE id=:user_id")
- result = connection.execute(t, user_id=12)
-
- For SQL statements where a colon is required verbatim, as within
- an inline string, use a backslash to escape::
-
- t = text("SELECT * FROM users WHERE name='\:username'")
-
- The :class:`_expression.TextClause`
- construct includes methods which can
- provide information about the bound parameters as well as the column
- values which would be returned from the textual statement, assuming
- it's an executable SELECT type of statement. The
- :meth:`_expression.TextClause.bindparams`
- method is used to provide bound
- parameter detail, and :meth:`_expression.TextClause.columns`
- method allows
- specification of return columns including names and types::
-
- t = text("SELECT * FROM users WHERE id=:user_id").\
- bindparams(user_id=7).\
- columns(id=Integer, name=String)
-
- for id, name in connection.execute(t):
- print(id, name)
-
- The :func:`_expression.text` construct is used in cases when
- a literal string SQL fragment is specified as part of a larger query,
- such as for the WHERE clause of a SELECT statement::
-
- s = select(users.c.id, users.c.name).where(text("id=:user_id"))
- result = connection.execute(s, user_id=12)
-
- :func:`_expression.text` is also used for the construction
- of a full, standalone statement using plain text.
- As such, SQLAlchemy refers
- to it as an :class:`.Executable` object and may be used
- like any other statement passed to an ``.execute()`` method.
-
- :param text:
- the text of the SQL statement to be created. Use ``:<param>``
- to specify bind parameters; they will be compiled to their
- engine-specific format.
-
- .. seealso::
-
- :ref:`sqlexpression_text` - in the Core tutorial
-
-
- """
- return TextClause(text)
-
@_generative
def bindparams(
self: SelfTextClause, *binds, **names_to_values
@@ -2204,40 +2200,6 @@ class False_(SingletonConstant, roles.ConstExprRole, ColumnElement):
@classmethod
def _instance(cls):
- """Return a :class:`.False_` construct.
-
- E.g.::
-
- >>> from sqlalchemy import false
- >>> print(select(t.c.x).where(false()))
- SELECT x FROM t WHERE false
-
- A backend which does not support true/false constants will render as
- an expression against 1 or 0::
-
- >>> print(select(t.c.x).where(false()))
- SELECT x FROM t WHERE 0 = 1
-
- The :func:`.true` and :func:`.false` constants also feature
- "short circuit" operation within an :func:`.and_` or :func:`.or_`
- conjunction::
-
- >>> print(select(t.c.x).where(or_(t.c.x > 5, true())))
- SELECT x FROM t WHERE true
-
- >>> print(select(t.c.x).where(and_(t.c.x > 5, false())))
- SELECT x FROM t WHERE false
-
- .. versionchanged:: 0.9 :func:`.true` and :func:`.false` feature
- better integrated behavior within conjunctions and on dialects
- that don't support true/false constants.
-
- .. seealso::
-
- :func:`.true`
-
- """
-
return False_()
@@ -2272,40 +2234,6 @@ class True_(SingletonConstant, roles.ConstExprRole, ColumnElement):
@classmethod
def _instance(cls):
- """Return a constant :class:`.True_` construct.
-
- E.g.::
-
- >>> from sqlalchemy import true
- >>> print(select(t.c.x).where(true()))
- SELECT x FROM t WHERE true
-
- A backend which does not support true/false constants will render as
- an expression against 1 or 0::
-
- >>> print(select(t.c.x).where(true()))
- SELECT x FROM t WHERE 1 = 1
-
- The :func:`.true` and :func:`.false` constants also feature
- "short circuit" operation within an :func:`.and_` or :func:`.or_`
- conjunction::
-
- >>> print(select(t.c.x).where(or_(t.c.x > 5, true())))
- SELECT x FROM t WHERE true
-
- >>> print(select(t.c.x).where(and_(t.c.x > 5, false())))
- SELECT x FROM t WHERE false
-
- .. versionchanged:: 0.9 :func:`.true` and :func:`.false` feature
- better integrated behavior within conjunctions and on dialects
- that don't support true/false constants.
-
- .. seealso::
-
- :func:`.false`
-
- """
-
return True_()
@@ -2334,15 +2262,23 @@ class ClauseList(
("operator", InternalTraversal.dp_operator),
]
- def __init__(self, *clauses, **kwargs):
- self.operator = kwargs.pop("operator", operators.comma_op)
- self.group = kwargs.pop("group", True)
- self.group_contents = kwargs.pop("group_contents", True)
- if kwargs.pop("_flatten_sub_clauses", False):
+ def __init__(
+ self,
+ *clauses,
+ operator=operators.comma_op,
+ group=True,
+ group_contents=True,
+ _flatten_sub_clauses=False,
+ _literal_as_text_role: Type[roles.SQLRole] = roles.WhereHavingRole,
+ ):
+ self.operator = operator
+ self.group = group
+ self.group_contents = group_contents
+ if _flatten_sub_clauses:
clauses = util.flatten_iterator(clauses)
- self._text_converter_role = text_converter_role = kwargs.pop(
- "_literal_as_text_role", roles.WhereHavingRole
- )
+ self._text_converter_role: Type[roles.SQLRole] = _literal_as_text_role
+ text_converter_role: Type[roles.SQLRole] = _literal_as_text_role
+
if self.group_contents:
self.clauses = [
coercions.expect(
@@ -2404,7 +2340,7 @@ class ClauseList(
return self
-class BooleanClauseList(ClauseList, ColumnElement):
+class BooleanClauseList(ClauseList, ColumnElement[bool]):
__visit_name__ = "clauselist"
inherit_cache = True
@@ -2531,60 +2467,7 @@ class BooleanClauseList(ClauseList, ColumnElement):
def and_(cls, *clauses):
r"""Produce a conjunction of expressions joined by ``AND``.
- E.g.::
-
- from sqlalchemy import and_
-
- stmt = select(users_table).where(
- and_(
- users_table.c.name == 'wendy',
- users_table.c.enrolled == True
- )
- )
-
- The :func:`.and_` conjunction is also available using the
- Python ``&`` operator (though note that compound expressions
- need to be parenthesized in order to function with Python
- operator precedence behavior)::
-
- stmt = select(users_table).where(
- (users_table.c.name == 'wendy') &
- (users_table.c.enrolled == True)
- )
-
- The :func:`.and_` operation is also implicit in some cases;
- the :meth:`_expression.Select.where`
- method for example can be invoked multiple
- times against a statement, which will have the effect of each
- clause being combined using :func:`.and_`::
-
- stmt = select(users_table).\
- where(users_table.c.name == 'wendy').\
- where(users_table.c.enrolled == True)
-
- The :func:`.and_` construct must be given at least one positional
- argument in order to be valid; a :func:`.and_` construct with no
- arguments is ambiguous. To produce an "empty" or dynamically
- generated :func:`.and_` expression, from a given list of expressions,
- a "default" element of ``True`` should be specified::
-
- criteria = and_(True, *expressions)
-
- The above expression will compile to SQL as the expression ``true``
- or ``1 = 1``, depending on backend, if no other expressions are
- present. If expressions are present, then the ``True`` value is
- ignored as it does not affect the outcome of an AND expression that
- has other elements.
-
- .. deprecated:: 1.4 The :func:`.and_` element now requires that at
- least one argument is passed; creating the :func:`.and_` construct
- with no arguments is deprecated, and will emit a deprecation warning
- while continuing to produce a blank SQL string.
-
- .. seealso::
-
- :func:`.or_`
-
+ See :func:`_sql.and_` for full documentation.
"""
return cls._construct(
operators.and_, True_._singleton, False_._singleton, *clauses
@@ -2594,50 +2477,7 @@ class BooleanClauseList(ClauseList, ColumnElement):
def or_(cls, *clauses):
"""Produce a conjunction of expressions joined by ``OR``.
- E.g.::
-
- from sqlalchemy import or_
-
- stmt = select(users_table).where(
- or_(
- users_table.c.name == 'wendy',
- users_table.c.name == 'jack'
- )
- )
-
- The :func:`.or_` conjunction is also available using the
- Python ``|`` operator (though note that compound expressions
- need to be parenthesized in order to function with Python
- operator precedence behavior)::
-
- stmt = select(users_table).where(
- (users_table.c.name == 'wendy') |
- (users_table.c.name == 'jack')
- )
-
- The :func:`.or_` construct must be given at least one positional
- argument in order to be valid; a :func:`.or_` construct with no
- arguments is ambiguous. To produce an "empty" or dynamically
- generated :func:`.or_` expression, from a given list of expressions,
- a "default" element of ``False`` should be specified::
-
- or_criteria = or_(False, *expressions)
-
- The above expression will compile to SQL as the expression ``false``
- or ``0 = 1``, depending on backend, if no other expressions are
- present. If expressions are present, then the ``False`` value is
- ignored as it does not affect the outcome of an OR expression which
- has other elements.
-
- .. deprecated:: 1.4 The :func:`.or_` element now requires that at
- least one argument is passed; creating the :func:`.or_` construct
- with no arguments is deprecated, and will emit a deprecation warning
- while continuing to produce a blank SQL string.
-
- .. seealso::
-
- :func:`.and_`
-
+ See :func:`_sql.or_` for full documentation.
"""
return cls._construct(
operators.or_, False_._singleton, True_._singleton, *clauses
@@ -2669,32 +2509,9 @@ class Tuple(ClauseList, ColumnElement):
_traverse_internals = ClauseList._traverse_internals + []
@util.preload_module("sqlalchemy.sql.sqltypes")
- def __init__(self, *clauses, **kw):
- """Return a :class:`.Tuple`.
-
- Main usage is to produce a composite IN construct using
- :meth:`.ColumnOperators.in_` ::
-
- from sqlalchemy import tuple_
-
- tuple_(table.c.col1, table.c.col2).in_(
- [(1, 2), (5, 12), (10, 19)]
- )
-
- .. versionchanged:: 1.3.6 Added support for SQLite IN tuples.
-
- .. warning::
-
- The composite IN construct is not supported by all backends, and is
- currently known to work on PostgreSQL, MySQL, and SQLite.
- Unsupported backends will raise a subclass of
- :class:`~sqlalchemy.exc.DBAPIError` when such an expression is
- invoked.
-
- """
+ def __init__(self, *clauses, types=None):
sqltypes = util.preloaded.sql_sqltypes
- types = kw.pop("types", None)
if types is None:
clauses = [
coercions.expect(roles.ExpressionElementRole, c)
@@ -2716,7 +2533,7 @@ class Tuple(ClauseList, ColumnElement):
]
self.type = sqltypes.TupleType(*[arg.type for arg in clauses])
- super(Tuple, self).__init__(*clauses, **kw)
+ super(Tuple, self).__init__(*clauses)
@property
def _select_iterable(self):
@@ -2752,7 +2569,7 @@ class Tuple(ClauseList, ColumnElement):
return self
-class Case(ColumnElement):
+class Case(ColumnElement[_T]):
"""Represent a ``CASE`` expression.
:class:`.Case` is produced using the :func:`.case` factory function,
@@ -2785,127 +2602,10 @@ class Case(ColumnElement):
("else_", InternalTraversal.dp_clauseelement),
]
- def __init__(self, *whens, value=None, else_=None):
- r"""Produce a ``CASE`` expression.
-
- The ``CASE`` construct in SQL is a conditional object that
- acts somewhat analogously to an "if/then" construct in other
- languages. It returns an instance of :class:`.Case`.
-
- :func:`.case` in its usual form is passed a series of "when"
- constructs, that is, a list of conditions and results as tuples::
-
- from sqlalchemy import case
-
- stmt = select(users_table).\
- where(
- case(
- (users_table.c.name == 'wendy', 'W'),
- (users_table.c.name == 'jack', 'J'),
- else_='E'
- )
- )
-
- The above statement will produce SQL resembling::
-
- SELECT id, name FROM user
- WHERE CASE
- WHEN (name = :name_1) THEN :param_1
- WHEN (name = :name_2) THEN :param_2
- ELSE :param_3
- END
-
- When simple equality expressions of several values against a single
- parent column are needed, :func:`.case` also has a "shorthand" format
- used via the
- :paramref:`.case.value` parameter, which is passed a column
- expression to be compared. In this form, the :paramref:`.case.whens`
- parameter is passed as a dictionary containing expressions to be
- compared against keyed to result expressions. The statement below is
- equivalent to the preceding statement::
-
- stmt = select(users_table).\
- where(
- case(
- {"wendy": "W", "jack": "J"},
- value=users_table.c.name,
- else_='E'
- )
- )
-
- The values which are accepted as result values in
- :paramref:`.case.whens` as well as with :paramref:`.case.else_` are
- coerced from Python literals into :func:`.bindparam` constructs.
- SQL expressions, e.g. :class:`_expression.ColumnElement` constructs,
- are accepted
- as well. To coerce a literal string expression into a constant
- expression rendered inline, use the :func:`_expression.literal_column`
- construct,
- as in::
-
- from sqlalchemy import case, literal_column
-
- case(
- (
- orderline.c.qty > 100,
- literal_column("'greaterthan100'")
- ),
- (
- orderline.c.qty > 10,
- literal_column("'greaterthan10'")
- ),
- else_=literal_column("'lessthan10'")
- )
-
- The above will render the given constants without using bound
- parameters for the result values (but still for the comparison
- values), as in::
-
- CASE
- WHEN (orderline.qty > :qty_1) THEN 'greaterthan100'
- WHEN (orderline.qty > :qty_2) THEN 'greaterthan10'
- ELSE 'lessthan10'
- END
-
- :param \*whens: The criteria to be compared against,
- :paramref:`.case.whens` accepts two different forms, based on
- whether or not :paramref:`.case.value` is used.
-
- .. versionchanged:: 1.4 the :func:`_sql.case`
- function now accepts the series of WHEN conditions positionally
-
- In the first form, it accepts a list of 2-tuples; each 2-tuple
- consists of ``(<sql expression>, <value>)``, where the SQL
- expression is a boolean expression and "value" is a resulting value,
- e.g.::
-
- case(
- (users_table.c.name == 'wendy', 'W'),
- (users_table.c.name == 'jack', 'J')
- )
-
- In the second form, it accepts a Python dictionary of comparison
- values mapped to a resulting value; this form requires
- :paramref:`.case.value` to be present, and values will be compared
- using the ``==`` operator, e.g.::
-
- case(
- {"wendy": "W", "jack": "J"},
- value=users_table.c.name
- )
-
- :param value: An optional SQL expression which will be used as a
- fixed "comparison point" for candidate values within a dictionary
- passed to :paramref:`.case.whens`.
-
- :param else\_: An optional SQL expression which will be the evaluated
- result of the ``CASE`` construct if all expressions within
- :paramref:`.case.whens` evaluate to false. When omitted, most
- databases will produce a result of NULL if none of the "when"
- expressions evaluate to true.
+ # for case(), the type is derived from the whens. so for the moment
+ # users would have to cast() the case to get a specific type
-
- """
+ def __init__(self, *whens, value=None, else_=None):
whens = coercions._expression_collection_was_a_list(
"whens", "case", whens
@@ -2952,43 +2652,7 @@ class Case(ColumnElement):
)
-def literal_column(text, type_=None):
- r"""Produce a :class:`.ColumnClause` object that has the
- :paramref:`_expression.column.is_literal` flag set to True.
-
- :func:`_expression.literal_column` is similar to
- :func:`_expression.column`, except that
- it is more often used as a "standalone" column expression that renders
- exactly as stated; while :func:`_expression.column`
- stores a string name that
- will be assumed to be part of a table and may be quoted as such,
- :func:`_expression.literal_column` can be that,
- or any other arbitrary column-oriented
- expression.
-
- :param text: the text of the expression; can be any SQL expression.
- Quoting rules will not be applied. To specify a column-name expression
- which should be subject to quoting rules, use the :func:`column`
- function.
-
- :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine`
- object which will
- provide result-set translation and additional expression semantics for
- this column. If left as ``None`` the type will be :class:`.NullType`.
-
- .. seealso::
-
- :func:`_expression.column`
-
- :func:`_expression.text`
-
- :ref:`sqlexpression_literal_column`
-
- """
- return ColumnClause(text, type_=type_, is_literal=True)
-
-
-class Cast(WrapsColumnExpression, ColumnElement):
+class Cast(WrapsColumnExpression, ColumnElement[_T]):
"""Represent a ``CAST`` expression.
:class:`.Cast` is produced using the :func:`.cast` factory function,
@@ -3020,57 +2684,6 @@ class Cast(WrapsColumnExpression, ColumnElement):
]
def __init__(self, expression, type_):
- r"""Produce a ``CAST`` expression.
-
- :func:`.cast` returns an instance of :class:`.Cast`.
-
- E.g.::
-
- from sqlalchemy import cast, Numeric
-
- stmt = select(cast(product_table.c.unit_price, Numeric(10, 4)))
-
- The above statement will produce SQL resembling::
-
- SELECT CAST(unit_price AS NUMERIC(10, 4)) FROM product
-
- The :func:`.cast` function performs two distinct functions when
- used. The first is that it renders the ``CAST`` expression within
- the resulting SQL string. The second is that it associates the given
- type (e.g. :class:`.TypeEngine` class or instance) with the column
- expression on the Python side, which means the expression will take
- on the expression operator behavior associated with that type,
- as well as the bound-value handling and result-row-handling behavior
- of the type.
-
- .. versionchanged:: 0.9.0 :func:`.cast` now applies the given type
- to the expression such that it takes effect on the bound-value,
- e.g. the Python-to-database direction, in addition to the
- result handling, e.g. database-to-Python, direction.
-
- An alternative to :func:`.cast` is the :func:`.type_coerce` function.
- This function performs the second task of associating an expression
- with a specific type, but does not render the ``CAST`` expression
- in SQL.
-
- :param expression: A SQL expression, such as a
- :class:`_expression.ColumnElement`
- expression or a Python string which will be coerced into a bound
- literal value.
-
- :param type\_: A :class:`.TypeEngine` class or instance indicating
- the type to which the ``CAST`` should apply.
-
- .. seealso::
-
- :ref:`coretutorial_casts`
-
- :func:`.type_coerce` - an alternative to CAST that coerces the type
- on the Python side only, which is often sufficient to generate the
- correct SQL and data coercion.
-
-
- """
self.type = type_api.to_instance(type_)
self.clause = coercions.expect(
roles.ExpressionElementRole,
@@ -3089,7 +2702,7 @@ class Cast(WrapsColumnExpression, ColumnElement):
return self.clause
-class TypeCoerce(WrapsColumnExpression, ColumnElement):
+class TypeCoerce(WrapsColumnExpression, ColumnElement[_T]):
"""Represent a Python-side type-coercion wrapper.
:class:`.TypeCoerce` supplies the :func:`_expression.type_coerce`
@@ -3115,80 +2728,6 @@ class TypeCoerce(WrapsColumnExpression, ColumnElement):
]
def __init__(self, expression, type_):
- r"""Associate a SQL expression with a particular type, without rendering
- ``CAST``.
-
- E.g.::
-
- from sqlalchemy import type_coerce
-
- stmt = select(type_coerce(log_table.date_string, StringDateTime()))
-
- The above construct will produce a :class:`.TypeCoerce` object, which
- does not modify the rendering in any way on the SQL side, with the
- possible exception of a generated label if used in a columns clause
- context::
-
- SELECT date_string AS date_string FROM log
-
- When result rows are fetched, the ``StringDateTime`` type processor
- will be applied to result rows on behalf of the ``date_string`` column.
-
- .. note:: the :func:`.type_coerce` construct does not render any
- SQL syntax of its own, including that it does not imply
- parenthesization. Please use :meth:`.TypeCoerce.self_group`
- if explicit parenthesization is required.
-
- In order to provide a named label for the expression, use
- :meth:`_expression.ColumnElement.label`::
-
- stmt = select(
- type_coerce(log_table.date_string, StringDateTime()).label('date')
- )
-
-
- A type that features bound-value handling will also have that behavior
- take effect when literal values or :func:`.bindparam` constructs are
- passed to :func:`.type_coerce` as targets.
- For example, if a type implements the
- :meth:`.TypeEngine.bind_expression`
- method or :meth:`.TypeEngine.bind_processor` method or equivalent,
- these functions will take effect at statement compilation/execution
- time when a literal value is passed, as in::
-
- # bound-value handling of MyStringType will be applied to the
- # literal value "some string"
- stmt = select(type_coerce("some string", MyStringType))
-
- When using :func:`.type_coerce` with composed expressions, note that
- **parenthesis are not applied**. If :func:`.type_coerce` is being
- used in an operator context where the parenthesis normally present from
- CAST are necessary, use the :meth:`.TypeCoerce.self_group` method::
-
- >>> some_integer = column("someint", Integer)
- >>> some_string = column("somestr", String)
- >>> expr = type_coerce(some_integer + 5, String) + some_string
- >>> print(expr)
- someint + :someint_1 || somestr
- >>> expr = type_coerce(some_integer + 5, String).self_group() + some_string
- >>> print(expr)
- (someint + :someint_1) || somestr
-
- :param expression: A SQL expression, such as a
- :class:`_expression.ColumnElement`
- expression or a Python string which will be coerced into a bound
- literal value.
-
- :param type\_: A :class:`.TypeEngine` class or instance indicating
- the type to which the expression is coerced.
-
- .. seealso::
-
- :ref:`coretutorial_casts`
-
- :func:`.cast`
-
- """ # noqa
self.type = type_api.to_instance(type_)
self.clause = coercions.expect(
roles.ExpressionElementRole,
@@ -3222,7 +2761,7 @@ class TypeCoerce(WrapsColumnExpression, ColumnElement):
return self
-class Extract(ColumnElement):
+class Extract(ColumnElement[_T]):
"""Represent a SQL EXTRACT clause, ``extract(field FROM expr)``."""
__visit_name__ = "extract"
@@ -3232,44 +2771,7 @@ class Extract(ColumnElement):
("field", InternalTraversal.dp_string),
]
- def __init__(self, field, expr, **kwargs):
- """Return a :class:`.Extract` construct.
-
- This is typically available as :func:`.extract`
- as well as ``func.extract`` from the
- :data:`.func` namespace.
-
- :param field: The field to extract.
-
- :param expr: A column or Python scalar expression serving as the
- right side of the ``EXTRACT`` expression.
-
- E.g.::
-
- from sqlalchemy import extract
- from sqlalchemy import table, column
-
- logged_table = table("user",
- column("id"),
- column("date_created"),
- )
-
- stmt = select(logged_table.c.id).where(
- extract("YEAR", logged_table.c.date_created) == 2021
- )
-
- In the above example, the statement is used to select ids from the
- database where the ``YEAR`` component matches a specific value.
-
- Similarly, one can also select an extracted component::
-
- stmt = select(
- extract("YEAR", logged_table.c.date_created)
- ).where(logged_table.c.id == 1)
-
- The implementation of ``EXTRACT`` may vary across database backends.
- Users are reminded to consult their database documentation.
- """
+ def __init__(self, field, expr):
self.type = type_api.INTEGERTYPE
self.field = field
self.expr = coercions.expect(roles.ExpressionElementRole, expr)
@@ -3314,10 +2816,10 @@ class _textual_label_reference(ColumnElement):
@util.memoized_property
def _text_clause(self):
- return TextClause._create_text(self.element)
+ return TextClause(self.element)
-class UnaryExpression(ColumnElement):
+class UnaryExpression(ColumnElement[_T]):
"""Define a 'unary' expression.
A unary expression has a single column expression
@@ -3344,7 +2846,7 @@ class UnaryExpression(ColumnElement):
element,
operator=None,
modifier=None,
- type_=None,
+ type_: Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"] = None,
wraps_column_expression=False,
):
self.operator = operator
@@ -3353,51 +2855,11 @@ class UnaryExpression(ColumnElement):
self.element = element.self_group(
against=self.operator or self.modifier
)
- self.type = type_api.to_instance(type_)
+ self.type: TypeEngine[_T] = type_api.to_instance(type_)
self.wraps_column_expression = wraps_column_expression
@classmethod
def _create_nulls_first(cls, column):
- """Produce the ``NULLS FIRST`` modifier for an ``ORDER BY`` expression.
-
- :func:`.nulls_first` is intended to modify the expression produced
- by :func:`.asc` or :func:`.desc`, and indicates how NULL values
- should be handled when they are encountered during ordering::
-
-
- from sqlalchemy import desc, nulls_first
-
- stmt = select(users_table).order_by(
- nulls_first(desc(users_table.c.name)))
-
- The SQL expression from the above would resemble::
-
- SELECT id, name FROM user ORDER BY name DESC NULLS FIRST
-
- Like :func:`.asc` and :func:`.desc`, :func:`.nulls_first` is typically
- invoked from the column expression itself using
- :meth:`_expression.ColumnElement.nulls_first`,
- rather than as its standalone
- function version, as in::
-
- stmt = select(users_table).order_by(
- users_table.c.name.desc().nulls_first())
-
- .. versionchanged:: 1.4 :func:`.nulls_first` is renamed from
- :func:`.nullsfirst` in previous releases.
- The previous name remains available for backwards compatibility.
-
- .. seealso::
-
- :func:`.asc`
-
- :func:`.desc`
-
- :func:`.nulls_last`
-
- :meth:`_expression.Select.order_by`
-
- """
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
modifier=operators.nulls_first_op,
@@ -3406,46 +2868,6 @@ class UnaryExpression(ColumnElement):
@classmethod
def _create_nulls_last(cls, column):
- """Produce the ``NULLS LAST`` modifier for an ``ORDER BY`` expression.
-
- :func:`.nulls_last` is intended to modify the expression produced
- by :func:`.asc` or :func:`.desc`, and indicates how NULL values
- should be handled when they are encountered during ordering::
-
-
- from sqlalchemy import desc, nulls_last
-
- stmt = select(users_table).order_by(
- nulls_last(desc(users_table.c.name)))
-
- The SQL expression from the above would resemble::
-
- SELECT id, name FROM user ORDER BY name DESC NULLS LAST
-
- Like :func:`.asc` and :func:`.desc`, :func:`.nulls_last` is typically
- invoked from the column expression itself using
- :meth:`_expression.ColumnElement.nulls_last`,
- rather than as its standalone
- function version, as in::
-
- stmt = select(users_table).order_by(
- users_table.c.name.desc().nulls_last())
-
- .. versionchanged:: 1.4 :func:`.nulls_last` is renamed from
- :func:`.nullslast` in previous releases.
- The previous name remains available for backwards compatibility.
-
- .. seealso::
-
- :func:`.asc`
-
- :func:`.desc`
-
- :func:`.nulls_first`
-
- :meth:`_expression.Select.order_by`
-
- """
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
modifier=operators.nulls_last_op,
@@ -3454,41 +2876,6 @@ class UnaryExpression(ColumnElement):
@classmethod
def _create_desc(cls, column):
- """Produce a descending ``ORDER BY`` clause element.
-
- e.g.::
-
- from sqlalchemy import desc
-
- stmt = select(users_table).order_by(desc(users_table.c.name))
-
- will produce SQL as::
-
- SELECT id, name FROM user ORDER BY name DESC
-
- The :func:`.desc` function is a standalone version of the
- :meth:`_expression.ColumnElement.desc`
- method available on all SQL expressions,
- e.g.::
-
-
- stmt = select(users_table).order_by(users_table.c.name.desc())
-
- :param column: A :class:`_expression.ColumnElement` (e.g.
- scalar SQL expression)
- with which to apply the :func:`.desc` operation.
-
- .. seealso::
-
- :func:`.asc`
-
- :func:`.nulls_first`
-
- :func:`.nulls_last`
-
- :meth:`_expression.Select.order_by`
-
- """
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
modifier=operators.desc_op,
@@ -3497,40 +2884,6 @@ class UnaryExpression(ColumnElement):
@classmethod
def _create_asc(cls, column):
- """Produce an ascending ``ORDER BY`` clause element.
-
- e.g.::
-
- from sqlalchemy import asc
- stmt = select(users_table).order_by(asc(users_table.c.name))
-
- will produce SQL as::
-
- SELECT id, name FROM user ORDER BY name ASC
-
- The :func:`.asc` function is a standalone version of the
- :meth:`_expression.ColumnElement.asc`
- method available on all SQL expressions,
- e.g.::
-
-
- stmt = select(users_table).order_by(users_table.c.name.asc())
-
- :param column: A :class:`_expression.ColumnElement` (e.g.
- scalar SQL expression)
- with which to apply the :func:`.asc` operation.
-
- .. seealso::
-
- :func:`.desc`
-
- :func:`.nulls_first`
-
- :func:`.nulls_last`
-
- :meth:`_expression.Select.order_by`
-
- """
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
modifier=operators.asc_op,
@@ -3539,41 +2892,6 @@ class UnaryExpression(ColumnElement):
@classmethod
def _create_distinct(cls, expr):
- """Produce an column-expression-level unary ``DISTINCT`` clause.
-
- This applies the ``DISTINCT`` keyword to an individual column
- expression, and is typically contained within an aggregate function,
- as in::
-
- from sqlalchemy import distinct, func
- stmt = select(func.count(distinct(users_table.c.name)))
-
- The above would produce an expression resembling::
-
- SELECT COUNT(DISTINCT name) FROM user
-
- The :func:`.distinct` function is also available as a column-level
- method, e.g. :meth:`_expression.ColumnElement.distinct`, as in::
-
- stmt = select(func.count(users_table.c.name.distinct()))
-
- The :func:`.distinct` operator is different from the
- :meth:`_expression.Select.distinct` method of
- :class:`_expression.Select`,
- which produces a ``SELECT`` statement
- with ``DISTINCT`` applied to the result set as a whole,
- e.g. a ``SELECT DISTINCT`` expression. See that method for further
- information.
-
- .. seealso::
-
- :meth:`_expression.ColumnElement.distinct`
-
- :meth:`_expression.Select.distinct`
-
- :data:`.func`
-
- """
expr = coercions.expect(roles.ExpressionElementRole, expr)
return UnaryExpression(
expr,
@@ -3625,57 +2943,6 @@ class CollectionAggregate(UnaryExpression):
@classmethod
def _create_any(cls, expr):
- """Produce an ANY expression.
-
- For dialects such as that of PostgreSQL, this operator applies
- to usage of the :class:`_types.ARRAY` datatype, for that of
- MySQL, it may apply to a subquery. e.g.::
-
- # renders on PostgreSQL:
- # '5 = ANY (somearray)'
- expr = 5 == any_(mytable.c.somearray)
-
- # renders on MySQL:
- # '5 = ANY (SELECT value FROM table)'
- expr = 5 == any_(select(table.c.value))
-
- Comparison to NULL may work using ``None`` or :func:`_sql.null`::
-
- None == any_(mytable.c.somearray)
-
- The any_() / all_() operators also feature a special "operand flipping"
- behavior such that if any_() / all_() are used on the left side of a
- comparison using a standalone operator such as ``==``, ``!=``, etc.
- (not including operator methods such as
- :meth:`_sql.ColumnOperators.is_`) the rendered expression is flipped::
-
- # would render '5 = ANY (column)`
- any_(mytable.c.column) == 5
-
- Or with ``None``, which note will not perform
- the usual step of rendering "IS" as is normally the case for NULL::
-
- # would render 'NULL = ANY(somearray)'
- any_(mytable.c.somearray) == None
-
- .. versionchanged:: 1.4.26 repaired the use of any_() / all_()
- comparing to NULL on the right side to be flipped to the left.
-
- The column-level :meth:`_sql.ColumnElement.any_` method (not to be
- confused with :class:`_types.ARRAY` level
- :meth:`_types.ARRAY.Comparator.any`) is shorthand for
- ``any_(col)``::
-
- 5 = mytable.c.somearray.any_()
-
- .. seealso::
-
- :meth:`_sql.ColumnOperators.any_`
-
- :func:`_expression.all_`
-
- """
-
expr = coercions.expect(roles.ExpressionElementRole, expr)
expr = expr.self_group()
@@ -3688,56 +2955,6 @@ class CollectionAggregate(UnaryExpression):
@classmethod
def _create_all(cls, expr):
- """Produce an ALL expression.
-
- For dialects such as that of PostgreSQL, this operator applies
- to usage of the :class:`_types.ARRAY` datatype, for that of
- MySQL, it may apply to a subquery. e.g.::
-
- # renders on PostgreSQL:
- # '5 = ALL (somearray)'
- expr = 5 == all_(mytable.c.somearray)
-
- # renders on MySQL:
- # '5 = ALL (SELECT value FROM table)'
- expr = 5 == all_(select(table.c.value))
-
- Comparison to NULL may work using ``None``::
-
- None == all_(mytable.c.somearray)
-
- The any_() / all_() operators also feature a special "operand flipping"
- behavior such that if any_() / all_() are used on the left side of a
- comparison using a standalone operator such as ``==``, ``!=``, etc.
- (not including operator methods such as
- :meth:`_sql.ColumnOperators.is_`) the rendered expression is flipped::
-
- # would render '5 = ALL (column)`
- all_(mytable.c.column) == 5
-
- Or with ``None``, which note will not perform
- the usual step of rendering "IS" as is normally the case for NULL::
-
- # would render 'NULL = ALL(somearray)'
- all_(mytable.c.somearray) == None
-
- .. versionchanged:: 1.4.26 repaired the use of any_() / all_()
- comparing to NULL on the right side to be flipped to the left.
-
- The column-level :meth:`_sql.ColumnElement.all_` method (not to be
- confused with :class:`_types.ARRAY` level
- :meth:`_types.ARRAY.Comparator.all`) is shorthand for
- ``all_(col)``::
-
- 5 == mytable.c.somearray.all_()
-
- .. seealso::
-
- :meth:`_sql.ColumnOperators.all_`
-
- :func:`_expression.any_`
-
- """
expr = coercions.expect(roles.ExpressionElementRole, expr)
expr = expr.self_group()
return CollectionAggregate(
@@ -3792,7 +3009,7 @@ class AsBoolean(WrapsColumnExpression, UnaryExpression):
return AsBoolean(self.element, self.negate, self.operator)
-class BinaryExpression(ColumnElement):
+class BinaryExpression(ColumnElement[_T]):
"""Represent an expression that is ``LEFT <operator> RIGHT``.
A :class:`.BinaryExpression` is generated automatically
@@ -3827,7 +3044,15 @@ class BinaryExpression(ColumnElement):
"""
def __init__(
- self, left, right, operator, type_=None, negate=None, modifiers=None
+ self,
+ left: ColumnElement,
+ right: Union[ColumnElement, ClauseList],
+ operator,
+ type_: Optional[
+ Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]
+ ] = None,
+ negate=None,
+ modifiers=None,
):
# allow compatibility with libraries that
# refer to BinaryExpression directly and pass strings
@@ -3838,7 +3063,7 @@ class BinaryExpression(ColumnElement):
self.left = left.self_group(against=operator)
self.right = right.self_group(against=operator)
self.operator = operator
- self.type = type_api.to_instance(type_)
+ self.type: TypeEngine[_T] = type_api.to_instance(type_)
self.negate = negate
self._is_implicitly_boolean = operators.is_boolean(operator)
@@ -3855,6 +3080,13 @@ class BinaryExpression(ColumnElement):
__nonzero__ = __bool__
+ if typing.TYPE_CHECKING:
+
+ def __invert__(
+ self: "BinaryExpression[_T]",
+ ) -> "BinaryExpression[_T]":
+ ...
+
@property
def is_comparison(self):
return operators.is_comparison(self.operator)
@@ -3996,7 +3228,7 @@ RANGE_UNBOUNDED = util.symbol("RANGE_UNBOUNDED")
RANGE_CURRENT = util.symbol("RANGE_CURRENT")
-class Over(ColumnElement):
+class Over(ColumnElement[_T]):
"""Represent an OVER clause.
This is a special operator against a so-called
@@ -4026,88 +3258,6 @@ class Over(ColumnElement):
def __init__(
self, element, partition_by=None, order_by=None, range_=None, rows=None
):
- r"""Produce an :class:`.Over` object against a function.
-
- Used against aggregate or so-called "window" functions,
- for database backends that support window functions.
-
- :func:`_expression.over` is usually called using
- the :meth:`.FunctionElement.over` method, e.g.::
-
- func.row_number().over(order_by=mytable.c.some_column)
-
- Would produce::
-
- ROW_NUMBER() OVER(ORDER BY some_column)
-
- Ranges are also possible using the :paramref:`.expression.over.range_`
- and :paramref:`.expression.over.rows` parameters. These
- mutually-exclusive parameters each accept a 2-tuple, which contains
- a combination of integers and None::
-
- func.row_number().over(
- order_by=my_table.c.some_column, range_=(None, 0))
-
- The above would produce::
-
- ROW_NUMBER() OVER(ORDER BY some_column
- RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
-
- A value of ``None`` indicates "unbounded", a
- value of zero indicates "current row", and negative / positive
- integers indicate "preceding" and "following":
-
- * RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING::
-
- func.row_number().over(order_by='x', range_=(-5, 10))
-
- * ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW::
-
- func.row_number().over(order_by='x', rows=(None, 0))
-
- * RANGE BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING::
-
- func.row_number().over(order_by='x', range_=(-2, None))
-
- * RANGE BETWEEN 1 FOLLOWING AND 3 FOLLOWING::
-
- func.row_number().over(order_by='x', range_=(1, 3))
-
- .. versionadded:: 1.1 support for RANGE / ROWS within a window
-
-
- :param element: a :class:`.FunctionElement`, :class:`.WithinGroup`,
- or other compatible construct.
- :param partition_by: a column element or string, or a list
- of such, that will be used as the PARTITION BY clause
- of the OVER construct.
- :param order_by: a column element or string, or a list
- of such, that will be used as the ORDER BY clause
- of the OVER construct.
- :param range\_: optional range clause for the window. This is a
- tuple value which can contain integer values or ``None``,
- and will render a RANGE BETWEEN PRECEDING / FOLLOWING clause.
-
- .. versionadded:: 1.1
-
- :param rows: optional rows clause for the window. This is a tuple
- value which can contain integer values or None, and will render
- a ROWS BETWEEN PRECEDING / FOLLOWING clause.
-
- .. versionadded:: 1.1
-
- This function is also available from the :data:`~.expression.func`
- construct itself via the :meth:`.FunctionElement.over` method.
-
- .. seealso::
-
- :ref:`tutorial_window_functions` - in the :ref:`unified_tutorial`
-
- :data:`.expression.func`
-
- :func:`_expression.within_group`
-
- """
self.element = element
if order_by is not None:
self.order_by = ClauseList(
@@ -4191,7 +3341,7 @@ class Over(ColumnElement):
)
-class WithinGroup(ColumnElement):
+class WithinGroup(ColumnElement[_T]):
"""Represent a WITHIN GROUP (ORDER BY) clause.
This is a special operator against so-called
@@ -4218,44 +3368,6 @@ class WithinGroup(ColumnElement):
order_by = None
def __init__(self, element, *order_by):
- r"""Produce a :class:`.WithinGroup` object against a function.
-
- Used against so-called "ordered set aggregate" and "hypothetical
- set aggregate" functions, including :class:`.percentile_cont`,
- :class:`.rank`, :class:`.dense_rank`, etc.
-
- :func:`_expression.within_group` is usually called using
- the :meth:`.FunctionElement.within_group` method, e.g.::
-
- from sqlalchemy import within_group
- stmt = select(
- department.c.id,
- func.percentile_cont(0.5).within_group(
- department.c.salary.desc()
- )
- )
-
- The above statement would produce SQL similar to
- ``SELECT department.id, percentile_cont(0.5)
- WITHIN GROUP (ORDER BY department.salary DESC)``.
-
- :param element: a :class:`.FunctionElement` construct, typically
- generated by :data:`~.expression.func`.
- :param \*order_by: one or more column elements that will be used
- as the ORDER BY clause of the WITHIN GROUP construct.
-
- .. versionadded:: 1.1
-
- .. seealso::
-
- :ref:`tutorial_functions_within_group` - in the
- :ref:`unified_tutorial`
-
- :data:`.expression.func`
-
- :func:`_expression.over`
-
- """
self.element = element
if order_by is not None:
self.order_by = ClauseList(
@@ -4332,31 +3444,6 @@ class FunctionFilter(ColumnElement):
criterion = None
def __init__(self, func, *criterion):
- """Produce a :class:`.FunctionFilter` object against a function.
-
- Used against aggregate and window functions,
- for database backends that support the "FILTER" clause.
-
- E.g.::
-
- from sqlalchemy import funcfilter
- funcfilter(func.count(1), MyClass.name == 'some name')
-
- Would produce "COUNT(1) FILTER (WHERE myclass.name = 'some name')".
-
- This function is also available from the :data:`~.expression.func`
- construct itself via the :meth:`.FunctionElement.filter` method.
-
- .. versionadded:: 1.0.0
-
- .. seealso::
-
- :ref:`tutorial_functions_within_group` - in the
- :ref:`unified_tutorial`
-
- :meth:`.FunctionElement.filter`
-
- """
self.func = func
self.filter(*criterion)
@@ -4431,7 +3518,7 @@ class FunctionFilter(ColumnElement):
)
-class Label(roles.LabeledColumnExprRole, ColumnElement):
+class Label(roles.LabeledColumnExprRole, ColumnElement[_T]):
"""Represents a column label (AS).
Represent a label, as typically applied to any column-level
@@ -4448,22 +3535,6 @@ class Label(roles.LabeledColumnExprRole, ColumnElement):
]
def __init__(self, name, element, type_=None):
- """Return a :class:`Label` object for the
- given :class:`_expression.ColumnElement`.
-
- A label changes the name of an element in the columns clause of a
- ``SELECT`` statement, typically via the ``AS`` SQL keyword.
-
- This functionality is more conveniently available via the
- :meth:`_expression.ColumnElement.label` method on
- :class:`_expression.ColumnElement`.
-
- :param name: label name
-
- :param obj: a :class:`_expression.ColumnElement`.
-
- """
-
orig_element = element
element = coercions.expect(
roles.ExpressionElementRole,
@@ -4583,7 +3654,7 @@ class Label(roles.LabeledColumnExprRole, ColumnElement):
return self.key, e
-class NamedColumn(ColumnElement):
+class NamedColumn(ColumnElement[_T]):
is_literal = False
table = None
@@ -4673,7 +3744,7 @@ class ColumnClause(
roles.LabeledColumnExprRole,
roles.StrAsPlainColumnRole,
Immutable,
- NamedColumn,
+ NamedColumn[_T],
):
"""Represents a column expression from any textual string.
@@ -4728,101 +3799,18 @@ class ColumnClause(
_is_multiparam_column = False
- def __init__(self, text, type_=None, is_literal=False, _selectable=None):
- """Produce a :class:`.ColumnClause` object.
-
- The :class:`.ColumnClause` is a lightweight analogue to the
- :class:`_schema.Column` class. The :func:`_expression.column`
- function can
- be invoked with just a name alone, as in::
-
- from sqlalchemy import column
-
- id, name = column("id"), column("name")
- stmt = select(id, name).select_from("user")
-
- The above statement would produce SQL like::
-
- SELECT id, name FROM user
-
- Once constructed, :func:`_expression.column`
- may be used like any other SQL
- expression element such as within :func:`_expression.select`
- constructs::
-
- from sqlalchemy.sql import column
-
- id, name = column("id"), column("name")
- stmt = select(id, name).select_from("user")
-
- The text handled by :func:`_expression.column`
- is assumed to be handled
- like the name of a database column; if the string contains mixed case,
- special characters, or matches a known reserved word on the target
- backend, the column expression will render using the quoting
- behavior determined by the backend. To produce a textual SQL
- expression that is rendered exactly without any quoting,
- use :func:`_expression.literal_column` instead,
- or pass ``True`` as the
- value of :paramref:`_expression.column.is_literal`. Additionally,
- full SQL
- statements are best handled using the :func:`_expression.text`
- construct.
-
- :func:`_expression.column` can be used in a table-like
- fashion by combining it with the :func:`.table` function
- (which is the lightweight analogue to :class:`_schema.Table`
- ) to produce
- a working table construct with minimal boilerplate::
-
- from sqlalchemy import table, column, select
-
- user = table("user",
- column("id"),
- column("name"),
- column("description"),
- )
-
- stmt = select(user.c.description).where(user.c.name == 'wendy')
-
- A :func:`_expression.column` / :func:`.table`
- construct like that illustrated
- above can be created in an
- ad-hoc fashion and is not associated with any
- :class:`_schema.MetaData`, DDL, or events, unlike its
- :class:`_schema.Table` counterpart.
-
- .. versionchanged:: 1.0.0 :func:`_expression.column` can now
- be imported from the plain ``sqlalchemy`` namespace like any
- other SQL element.
-
- :param text: the text of the element.
-
- :param type: :class:`_types.TypeEngine` object which can associate
- this :class:`.ColumnClause` with a type.
-
- :param is_literal: if True, the :class:`.ColumnClause` is assumed to
- be an exact expression that will be delivered to the output with no
- quoting rules applied regardless of case sensitive settings. the
- :func:`_expression.literal_column()` function essentially invokes
- :func:`_expression.column` while passing ``is_literal=True``.
-
- .. seealso::
-
- :class:`_schema.Column`
-
- :func:`_expression.literal_column`
-
- :func:`.table`
-
- :func:`_expression.text`
-
- :ref:`sqlexpression_literal_column`
-
- """
+ def __init__(
+ self,
+ text: str,
+ type_: Optional[
+ Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]
+ ] = None,
+ is_literal: bool = False,
+ _selectable: Optional["FromClause"] = None,
+ ):
self.key = self.name = text
self.table = _selectable
- self.type = type_api.to_instance(type_)
+ self.type: TypeEngine[_T] = type_api.to_instance(type_)
self.is_literal = is_literal
def get_children(self, column_tables=False, **kw):
@@ -5010,6 +3998,16 @@ class CollationClause(ColumnElement):
_traverse_internals = [("collation", InternalTraversal.dp_string)]
+ @classmethod
+ def _create_collation_expression(cls, expression, collation):
+ expr = coercions.expect(roles.ExpressionElementRole, expression)
+ return BinaryExpression(
+ expr,
+ CollationClause(collation),
+ operators.collate,
+ type_=expression.type,
+ )
+
def __init__(self, collation):
self.collation = collation
@@ -5280,7 +4278,7 @@ class _anonymous_label(_truncated_label):
@classmethod
def safe_construct(
cls, seed, body, enclosing_label=None, sanitize_key=False
- ):
+ ) -> "_anonymous_label":
if sanitize_key:
body = re.sub(r"[%\(\) \$]+", "_", body).strip("_")
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 54f67b930..680eae754 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -10,263 +10,143 @@
"""
-__all__ = [
- "Alias",
- "AliasedReturnsRows",
- "any_",
- "all_",
- "CacheKey",
- "ClauseElement",
- "ColumnCollection",
- "ColumnElement",
- "CompoundSelect",
- "Delete",
- "FromClause",
- "Insert",
- "Join",
- "Lateral",
- "LambdaElement",
- "StatementLambdaElement",
- "Select",
- "Selectable",
- "TableClause",
- "TableValuedAlias",
- "Update",
- "Values",
- "alias",
- "and_",
- "asc",
- "between",
- "bindparam",
- "case",
- "cast",
- "column",
- "custom_op",
- "cte",
- "delete",
- "desc",
- "distinct",
- "except_",
- "except_all",
- "exists",
- "extract",
- "func",
- "modifier",
- "collate",
- "insert",
- "intersect",
- "intersect_all",
- "join",
- "label",
- "lateral",
- "lambda_stmt",
- "literal",
- "literal_column",
- "not_",
- "null",
- "nulls_first",
- "nulls_last",
- "or_",
- "outparam",
- "outerjoin",
- "over",
- "select",
- "table",
- "text",
- "tuple_",
- "type_coerce",
- "quoted_name",
- "union",
- "union_all",
- "update",
- "quoted_name",
- "within_group",
- "Subquery",
- "TableSample",
- "tablesample",
- "values",
-]
+from ._dml_constructors import delete as delete
+from ._dml_constructors import insert as insert
+from ._dml_constructors import update as update
+from ._elements_constructors import all_ as all_
+from ._elements_constructors import and_ as and_
+from ._elements_constructors import any_ as any_
+from ._elements_constructors import asc as asc
+from ._elements_constructors import between as between
+from ._elements_constructors import bindparam as bindparam
+from ._elements_constructors import case as case
+from ._elements_constructors import cast as cast
+from ._elements_constructors import collate as collate
+from ._elements_constructors import column as column
+from ._elements_constructors import desc as desc
+from ._elements_constructors import distinct as distinct
+from ._elements_constructors import extract as extract
+from ._elements_constructors import false as false
+from ._elements_constructors import funcfilter as funcfilter
+from ._elements_constructors import label as label
+from ._elements_constructors import not_ as not_
+from ._elements_constructors import null as null
+from ._elements_constructors import nulls_first as nulls_first
+from ._elements_constructors import nulls_last as nulls_last
+from ._elements_constructors import or_ as or_
+from ._elements_constructors import outparam as outparam
+from ._elements_constructors import over as over
+from ._elements_constructors import text as text
+from ._elements_constructors import true as true
+from ._elements_constructors import tuple_ as tuple_
+from ._elements_constructors import type_coerce as type_coerce
+from ._elements_constructors import typing as typing
+from ._elements_constructors import within_group as within_group
+from ._selectable_constructors import alias as alias
+from ._selectable_constructors import cte as cte
+from ._selectable_constructors import except_ as except_
+from ._selectable_constructors import except_all as except_all
+from ._selectable_constructors import exists as exists
+from ._selectable_constructors import intersect as intersect
+from ._selectable_constructors import intersect_all as intersect_all
+from ._selectable_constructors import join as join
+from ._selectable_constructors import lateral as lateral
+from ._selectable_constructors import outerjoin as outerjoin
+from ._selectable_constructors import select as select
+from ._selectable_constructors import table as table
+from ._selectable_constructors import tablesample as tablesample
+from ._selectable_constructors import union as union
+from ._selectable_constructors import union_all as union_all
+from ._selectable_constructors import values as values
+from .base import _from_objects as _from_objects
+from .base import _select_iterables as _select_iterables
+from .base import ColumnCollection as ColumnCollection
+from .base import Executable as Executable
+from .cache_key import CacheKey as CacheKey
+from .dml import Delete as Delete
+from .dml import Insert as Insert
+from .dml import Update as Update
+from .dml import UpdateBase as UpdateBase
+from .dml import ValuesBase as ValuesBase
+from .elements import _truncated_label as _truncated_label
+from .elements import BinaryExpression as BinaryExpression
+from .elements import BindParameter as BindParameter
+from .elements import BooleanClauseList as BooleanClauseList
+from .elements import Case as Case
+from .elements import Cast as Cast
+from .elements import ClauseElement as ClauseElement
+from .elements import ClauseList as ClauseList
+from .elements import CollectionAggregate as CollectionAggregate
+from .elements import ColumnClause as ColumnClause
+from .elements import ColumnElement as ColumnElement
+from .elements import Extract as Extract
+from .elements import False_ as False_
+from .elements import FunctionFilter as FunctionFilter
+from .elements import Grouping as Grouping
+from .elements import Label as Label
+from .elements import literal as literal
+from .elements import literal_column as literal_column
+from .elements import Null as Null
+from .elements import Over as Over
+from .elements import quoted_name as quoted_name
+from .elements import ReleaseSavepointClause as ReleaseSavepointClause
+from .elements import RollbackToSavepointClause as RollbackToSavepointClause
+from .elements import SavepointClause as SavepointClause
+from .elements import TextClause as TextClause
+from .elements import True_ as True_
+from .elements import Tuple as Tuple
+from .elements import TypeClause as TypeClause
+from .elements import TypeCoerce as TypeCoerce
+from .elements import UnaryExpression as UnaryExpression
+from .elements import WithinGroup as WithinGroup
+from .functions import func as func
+from .functions import Function as Function
+from .functions import FunctionElement as FunctionElement
+from .functions import modifier as modifier
+from .lambdas import lambda_stmt as lambda_stmt
+from .lambdas import LambdaElement as LambdaElement
+from .lambdas import StatementLambdaElement as StatementLambdaElement
+from .operators import ColumnOperators as ColumnOperators
+from .operators import custom_op as custom_op
+from .operators import Operators as Operators
+from .selectable import Alias as Alias
+from .selectable import AliasedReturnsRows as AliasedReturnsRows
+from .selectable import CompoundSelect as CompoundSelect
+from .selectable import CTE as CTE
+from .selectable import Exists as Exists
+from .selectable import FromClause as FromClause
+from .selectable import FromGrouping as FromGrouping
+from .selectable import GenerativeSelect as GenerativeSelect
+from .selectable import HasCTE as HasCTE
+from .selectable import HasPrefixes as HasPrefixes
+from .selectable import HasSuffixes as HasSuffixes
+from .selectable import Join as Join
+from .selectable import LABEL_STYLE_DEFAULT as LABEL_STYLE_DEFAULT
+from .selectable import LABEL_STYLE_NONE as LABEL_STYLE_NONE
+from .selectable import Lateral as Lateral
+from .selectable import ReturnsRows as ReturnsRows
+from .selectable import ScalarSelect as ScalarSelect
+from .selectable import Select as Select
+from .selectable import Selectable as Selectable
+from .selectable import SelectBase as SelectBase
+from .selectable import Subquery as Subquery
+from .selectable import TableClause as TableClause
+from .selectable import TableSample as TableSample
+from .selectable import TableValuedAlias as TableValuedAlias
+from .selectable import TextAsFrom as TextAsFrom
+from .selectable import TextualSelect as TextualSelect
+from .selectable import Values as Values
+from .visitors import Visitable as Visitable
-from typing import Callable
+if True:
+ # work around zimports
+ from .selectable import (
+ LABEL_STYLE_DISAMBIGUATE_ONLY as LABEL_STYLE_DISAMBIGUATE_ONLY,
+ )
+ from .selectable import (
+ LABEL_STYLE_TABLENAME_PLUS_COL as LABEL_STYLE_TABLENAME_PLUS_COL,
+ )
-from .base import _from_objects
-from .base import _select_iterables
-from .base import ColumnCollection
-from .base import Executable
-from .cache_key import CacheKey
-from .dml import Delete
-from .dml import Insert
-from .dml import Update
-from .dml import UpdateBase
-from .dml import ValuesBase
-from .elements import _truncated_label
-from .elements import between
-from .elements import BinaryExpression
-from .elements import BindParameter
-from .elements import BooleanClauseList
-from .elements import Case
-from .elements import Cast
-from .elements import ClauseElement
-from .elements import ClauseList
-from .elements import collate
-from .elements import CollectionAggregate
-from .elements import ColumnClause
-from .elements import ColumnElement
-from .elements import Extract
-from .elements import False_
-from .elements import FunctionFilter
-from .elements import Grouping
-from .elements import Label
-from .elements import literal
-from .elements import literal_column
-from .elements import not_
-from .elements import Null
-from .elements import outparam
-from .elements import Over
-from .elements import quoted_name
-from .elements import ReleaseSavepointClause
-from .elements import RollbackToSavepointClause
-from .elements import SavepointClause
-from .elements import TextClause
-from .elements import True_
-from .elements import Tuple
-from .elements import TypeClause
-from .elements import TypeCoerce
-from .elements import UnaryExpression
-from .elements import WithinGroup
-from .functions import func
-from .functions import Function
-from .functions import FunctionElement
-from .functions import modifier
-from .lambdas import lambda_stmt
-from .lambdas import LambdaElement
-from .lambdas import StatementLambdaElement
-from .operators import ColumnOperators
-from .operators import custom_op
-from .operators import Operators
-from .selectable import Alias
-from .selectable import AliasedReturnsRows
-from .selectable import CompoundSelect
-from .selectable import CTE
-from .selectable import Exists
-from .selectable import FromClause
-from .selectable import FromGrouping
-from .selectable import GenerativeSelect
-from .selectable import HasCTE
-from .selectable import HasPrefixes
-from .selectable import HasSuffixes
-from .selectable import Join
-from .selectable import LABEL_STYLE_DEFAULT
-from .selectable import LABEL_STYLE_DISAMBIGUATE_ONLY
-from .selectable import LABEL_STYLE_NONE
-from .selectable import LABEL_STYLE_TABLENAME_PLUS_COL
-from .selectable import Lateral
-from .selectable import ReturnsRows
-from .selectable import ScalarSelect
-from .selectable import Select
-from .selectable import Selectable
-from .selectable import SelectBase
-from .selectable import Subquery
-from .selectable import TableClause
-from .selectable import TableSample
-from .selectable import TableValuedAlias
-from .selectable import TextAsFrom
-from .selectable import TextualSelect
-from .selectable import Values
-from .visitors import Visitable
-from ..util.langhelpers import public_factory
-
-# TODO: proposal is to remove public_factory and replace with traditional
-# functions exported here.
-
-all_ = public_factory(CollectionAggregate._create_all, ".sql.expression.all_")
-any_ = public_factory(CollectionAggregate._create_any, ".sql.expression.any_")
-and_ = public_factory(BooleanClauseList.and_, ".sql.expression.and_")
-alias = public_factory(Alias._factory, ".sql.expression.alias")
-tablesample = public_factory(
- TableSample._factory, ".sql.expression.tablesample"
-)
-lateral = public_factory(Lateral._factory, ".sql.expression.lateral")
-or_ = public_factory(BooleanClauseList.or_, ".sql.expression.or_")
-bindparam = public_factory(BindParameter, ".sql.expression.bindparam")
-select = public_factory(Select._create, ".sql.expression.select")
-text = public_factory(TextClause._create_text, ".sql.expression.text")
-table = public_factory(TableClause, ".sql.expression.table")
-column = public_factory(ColumnClause, ".sql.expression.column")
-over = public_factory(Over, ".sql.expression.over")
-within_group = public_factory(WithinGroup, ".sql.expression.within_group")
-label = public_factory(Label, ".sql.expression.label")
-case = public_factory(Case, ".sql.expression.case")
-cast = public_factory(Cast, ".sql.expression.cast")
-cte = public_factory(CTE._factory, ".sql.expression.cte")
-values = public_factory(Values, ".sql.expression.values")
-extract = public_factory(Extract, ".sql.expression.extract")
-tuple_ = public_factory(Tuple, ".sql.expression.tuple_")
-except_ = public_factory(
- CompoundSelect._create_except, ".sql.expression.except_"
-)
-except_all = public_factory(
- CompoundSelect._create_except_all, ".sql.expression.except_all"
-)
-intersect = public_factory(
- CompoundSelect._create_intersect, ".sql.expression.intersect"
-)
-intersect_all = public_factory(
- CompoundSelect._create_intersect_all, ".sql.expression.intersect_all"
-)
-union = public_factory(CompoundSelect._create_union, ".sql.expression.union")
-union_all = public_factory(
- CompoundSelect._create_union_all, ".sql.expression.union_all"
-)
-exists = public_factory(Exists, ".sql.expression.exists")
-nulls_first = public_factory(
- UnaryExpression._create_nulls_first, ".sql.expression.nulls_first"
-)
-nullsfirst = nulls_first # deprecated 1.4; see #5435
-nulls_last = public_factory(
- UnaryExpression._create_nulls_last, ".sql.expression.nulls_last"
-)
-nullslast = nulls_last # deprecated 1.4; see #5435
-asc = public_factory(UnaryExpression._create_asc, ".sql.expression.asc")
-desc = public_factory(UnaryExpression._create_desc, ".sql.expression.desc")
-distinct = public_factory(
- UnaryExpression._create_distinct, ".sql.expression.distinct"
-)
-type_coerce = public_factory(TypeCoerce, ".sql.expression.type_coerce")
-true = public_factory(True_._instance, ".sql.expression.true")
-false = public_factory(False_._instance, ".sql.expression.false")
-null = public_factory(Null._instance, ".sql.expression.null")
-join = public_factory(Join._create_join, ".sql.expression.join")
-outerjoin = public_factory(Join._create_outerjoin, ".sql.expression.outerjoin")
-insert = public_factory(Insert, ".sql.expression.insert")
-update = public_factory(Update, ".sql.expression.update")
-delete = public_factory(Delete, ".sql.expression.delete")
-funcfilter = public_factory(FunctionFilter, ".sql.expression.funcfilter")
-
-
-# internal functions still being called from tests and the ORM,
-# these might be better off in some other namespace
-
-
-# old names for compatibility
-_Executable = Executable
-_BindParamClause = BindParameter
-_Label = Label
-_SelectBase = SelectBase
-_BinaryExpression = BinaryExpression
-_Cast = Cast
-_Null = Null
-_False = False_
-_True = True_
-_TextClause = TextClause
-_UnaryExpression = UnaryExpression
-_Case = Case
-_Tuple = Tuple
-_Over = Over
-_TypeClause = TypeClause
-_Extract = Extract
-_Exists = Exists
-_Grouping = Grouping
-_FromGrouping = FromGrouping
-_ScalarSelect = ScalarSelect
+nullsfirst = nulls_first
+nullslast = nulls_last
diff --git a/lib/sqlalchemy/sql/functions.py b/lib/sqlalchemy/sql/functions.py
index 3b6da7175..7a1e80889 100644
--- a/lib/sqlalchemy/sql/functions.py
+++ b/lib/sqlalchemy/sql/functions.py
@@ -567,7 +567,7 @@ class FunctionElement(Executable, ColumnElement, FromClause, Generative):
s = select(function_element)
"""
- s = Select._create(self)
+ s = Select(self)
if self._execution_options:
s = s.execution_options(**self._execution_options)
return s
diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py
index d7a5d9348..cf61f2637 100644
--- a/lib/sqlalchemy/sql/operators.py
+++ b/lib/sqlalchemy/sql/operators.py
@@ -10,32 +10,84 @@
"""Defines operators used in SQL expressions."""
-from operator import add
-from operator import and_
-from operator import contains
-from operator import eq
-from operator import floordiv
-from operator import ge
-from operator import getitem
-from operator import gt
-from operator import inv
-from operator import le
-from operator import lshift
-from operator import lt
-from operator import mod
-from operator import mul
-from operator import ne
-from operator import neg
-from operator import or_
-from operator import rshift
-from operator import sub
-from operator import truediv
+from operator import add as _uncast_add
+from operator import and_ as _uncast_and_
+from operator import contains as _uncast_contains
+from operator import eq as _uncast_eq
+from operator import floordiv as _uncast_floordiv
+from operator import ge as _uncast_ge
+from operator import getitem as _uncast_getitem
+from operator import gt as _uncast_gt
+from operator import inv as _uncast_inv
+from operator import le as _uncast_le
+from operator import lshift as _uncast_lshift
+from operator import lt as _uncast_lt
+from operator import mod as _uncast_mod
+from operator import mul as _uncast_mul
+from operator import ne as _uncast_ne
+from operator import neg as _uncast_neg
+from operator import or_ as _uncast_or_
+from operator import rshift as _uncast_rshift
+from operator import sub as _uncast_sub
+from operator import truediv as _uncast_truediv
+import typing
+from typing import Any
+from typing import Callable
+from typing import cast
+from typing import Generic
+from typing import Optional
+from typing import overload
+from typing import Type
+from typing import TypeVar
+from typing import Union
from .. import exc
from .. import util
-
-
-class Operators:
+from ..util.typing import Protocol
+
+if typing.TYPE_CHECKING:
+ from .elements import BinaryExpression
+ from .elements import ColumnElement
+ from .type_api import TypeEngine
+
+_OP_RETURN = TypeVar("_OP_RETURN", bound=Any, covariant=True)
+_T = TypeVar("_T", bound=Any)
+
+
+class OperatorType(Protocol):
+ """describe an op() function."""
+
+ __name__: str
+
+ def __call__(
+ self, left: "Operators[_OP_RETURN]", *other: Any, **kwargs: Any
+ ) -> "_OP_RETURN":
+ ...
+
+
+add = cast(OperatorType, _uncast_add)
+and_ = cast(OperatorType, _uncast_and_)
+contains = cast(OperatorType, _uncast_contains)
+eq = cast(OperatorType, _uncast_eq)
+floordiv = cast(OperatorType, _uncast_floordiv)
+ge = cast(OperatorType, _uncast_ge)
+getitem = cast(OperatorType, _uncast_getitem)
+gt = cast(OperatorType, _uncast_gt)
+inv = cast(OperatorType, _uncast_inv)
+le = cast(OperatorType, _uncast_le)
+lshift = cast(OperatorType, _uncast_lshift)
+lt = cast(OperatorType, _uncast_lt)
+mod = cast(OperatorType, _uncast_mod)
+mul = cast(OperatorType, _uncast_mul)
+ne = cast(OperatorType, _uncast_ne)
+neg = cast(OperatorType, _uncast_neg)
+or_ = cast(OperatorType, _uncast_or_)
+rshift = cast(OperatorType, _uncast_rshift)
+sub = cast(OperatorType, _uncast_sub)
+truediv = cast(OperatorType, _uncast_truediv)
+
+
+class Operators(Generic[_OP_RETURN]):
"""Base of comparison and logical operators.
Implements base methods
@@ -52,7 +104,7 @@ class Operators:
__slots__ = ()
- def __and__(self, other):
+ def __and__(self, other: Any) -> "Operators":
"""Implement the ``&`` operator.
When used with SQL expressions, results in an
@@ -76,7 +128,7 @@ class Operators:
"""
return self.operate(and_, other)
- def __or__(self, other):
+ def __or__(self, other: Any) -> "Operators":
"""Implement the ``|`` operator.
When used with SQL expressions, results in an
@@ -100,7 +152,7 @@ class Operators:
"""
return self.operate(or_, other)
- def __invert__(self):
+ def __invert__(self) -> "Operators":
"""Implement the ``~`` operator.
When used with SQL expressions, results in a
@@ -119,12 +171,14 @@ class Operators:
def op(
self,
- opstring,
- precedence=0,
- is_comparison=False,
- return_type=None,
+ opstring: Any,
+ precedence: int = 0,
+ is_comparison: bool = False,
+ return_type: Optional[
+ Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]
+ ] = None,
python_impl=None,
- ):
+ ) -> Callable[[Any], _OP_RETURN]:
"""Produce a generic operator function.
e.g.::
@@ -205,12 +259,14 @@ class Operators:
python_impl=python_impl,
)
- def against(other):
+ def against(other: Any) -> _OP_RETURN:
return operator(self, other)
return against
- def bool_op(self, opstring, precedence=0, python_impl=None):
+ def bool_op(
+ self, opstring: Any, precedence: int = 0, python_impl=None
+ ) -> Callable[[Any], _OP_RETURN]:
"""Return a custom boolean operator.
This method is shorthand for calling
@@ -230,7 +286,9 @@ class Operators:
python_impl=python_impl,
)
- def operate(self, op, *other, **kwargs):
+ def operate(
+ self, op: OperatorType, *other: Any, **kwargs: Any
+ ) -> _OP_RETURN:
r"""Operate on an argument.
This is the lowest level of operation, raises
@@ -258,7 +316,9 @@ class Operators:
__sa_operate__ = operate
- def reverse_operate(self, op, other, **kwargs):
+ def reverse_operate(
+ self, op: OperatorType, other: Any, **kwargs: Any
+ ) -> _OP_RETURN:
"""Reverse operate on an argument.
Usage is the same as :meth:`operate`.
@@ -267,7 +327,7 @@ class Operators:
raise NotImplementedError(str(op))
-class custom_op:
+class custom_op(OperatorType, Generic[_T]):
"""Represent a 'custom' operator.
:class:`.custom_op` is normally instantiated when the
@@ -307,12 +367,14 @@ class custom_op:
def __init__(
self,
- opstring,
- precedence=0,
- is_comparison=False,
- return_type=None,
- natural_self_precedent=False,
- eager_grouping=False,
+ opstring: str,
+ precedence: int = 0,
+ is_comparison: bool = False,
+ return_type: Optional[
+ Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]
+ ] = None,
+ natural_self_precedent: bool = False,
+ eager_grouping: bool = False,
python_impl=None,
):
self.opstring = opstring
@@ -325,13 +387,27 @@ class custom_op:
)
self.python_impl = python_impl
- def __eq__(self, other):
+ def __eq__(self, other: Any) -> bool:
return isinstance(other, custom_op) and other.opstring == self.opstring
- def __hash__(self):
+ def __hash__(self) -> int:
return id(self)
- def __call__(self, left, right, **kw):
+ @overload
+ def __call__(
+ self, left: "ColumnElement", right: Any, **kw
+ ) -> "BinaryExpression[_T]":
+ ...
+
+ @overload
+ def __call__(
+ self, left: "Operators[_OP_RETURN]", right: Any, **kw
+ ) -> _OP_RETURN:
+ ...
+
+ def __call__(
+ self, left: "Operators[_OP_RETURN]", right: Any, **kw
+ ) -> _OP_RETURN:
if hasattr(left, "__sa_operate__"):
return left.operate(self, right, **kw)
elif self.python_impl:
@@ -344,7 +420,7 @@ class custom_op:
)
-class ColumnOperators(Operators):
+class ColumnOperators(Operators[_OP_RETURN]):
"""Defines boolean, comparison, and other operators for
:class:`_expression.ColumnElement` expressions.
@@ -387,7 +463,19 @@ class ColumnOperators(Operators):
timetuple = None
"""Hack, allows datetime objects to be compared on the LHS."""
- def __lt__(self, other):
+ if typing.TYPE_CHECKING:
+
+ def operate(
+ self, op: OperatorType, *other: Any, **kwargs: Any
+ ) -> "ColumnOperators":
+ ...
+
+ def reverse_operate(
+ self, op: OperatorType, other: Any, **kwargs: Any
+ ) -> "ColumnOperators":
+ ...
+
+ def __lt__(self, other: Any) -> "ColumnOperators":
"""Implement the ``<`` operator.
In a column context, produces the clause ``a < b``.
@@ -395,7 +483,7 @@ class ColumnOperators(Operators):
"""
return self.operate(lt, other)
- def __le__(self, other):
+ def __le__(self, other: Any) -> "ColumnOperators":
"""Implement the ``<=`` operator.
In a column context, produces the clause ``a <= b``.
@@ -403,9 +491,10 @@ class ColumnOperators(Operators):
"""
return self.operate(le, other)
- __hash__ = Operators.__hash__
+ # TODO: not sure why we have this
+ __hash__ = Operators.__hash__ # type: ignore
- def __eq__(self, other):
+ def __eq__(self, other: Any) -> "ColumnOperators":
"""Implement the ``==`` operator.
In a column context, produces the clause ``a = b``.
@@ -414,7 +503,7 @@ class ColumnOperators(Operators):
"""
return self.operate(eq, other)
- def __ne__(self, other):
+ def __ne__(self, other: Any) -> "ColumnOperators":
"""Implement the ``!=`` operator.
In a column context, produces the clause ``a != b``.
@@ -423,7 +512,7 @@ class ColumnOperators(Operators):
"""
return self.operate(ne, other)
- def is_distinct_from(self, other):
+ def is_distinct_from(self, other: Any) -> "ColumnOperators":
"""Implement the ``IS DISTINCT FROM`` operator.
Renders "a IS DISTINCT FROM b" on most platforms;
@@ -434,7 +523,7 @@ class ColumnOperators(Operators):
"""
return self.operate(is_distinct_from, other)
- def is_not_distinct_from(self, other):
+ def is_not_distinct_from(self, other: Any) -> "ColumnOperators":
"""Implement the ``IS NOT DISTINCT FROM`` operator.
Renders "a IS NOT DISTINCT FROM b" on most platforms;
@@ -452,7 +541,7 @@ class ColumnOperators(Operators):
# deprecated 1.4; see #5435
isnot_distinct_from = is_not_distinct_from
- def __gt__(self, other):
+ def __gt__(self, other: Any) -> "ColumnOperators":
"""Implement the ``>`` operator.
In a column context, produces the clause ``a > b``.
@@ -460,7 +549,7 @@ class ColumnOperators(Operators):
"""
return self.operate(gt, other)
- def __ge__(self, other):
+ def __ge__(self, other: Any) -> "ColumnOperators":
"""Implement the ``>=`` operator.
In a column context, produces the clause ``a >= b``.
@@ -468,7 +557,7 @@ class ColumnOperators(Operators):
"""
return self.operate(ge, other)
- def __neg__(self):
+ def __neg__(self) -> "ColumnOperators":
"""Implement the ``-`` operator.
In a column context, produces the clause ``-a``.
@@ -476,10 +565,10 @@ class ColumnOperators(Operators):
"""
return self.operate(neg)
- def __contains__(self, other):
+ def __contains__(self, other: Any) -> "ColumnOperators":
return self.operate(contains, other)
- def __getitem__(self, index):
+ def __getitem__(self, index: Any) -> "ColumnOperators":
"""Implement the [] operator.
This can be used by some database-specific types
@@ -488,7 +577,7 @@ class ColumnOperators(Operators):
"""
return self.operate(getitem, index)
- def __lshift__(self, other):
+ def __lshift__(self, other: Any) -> "ColumnOperators":
"""implement the << operator.
Not used by SQLAlchemy core, this is provided
@@ -497,7 +586,7 @@ class ColumnOperators(Operators):
"""
return self.operate(lshift, other)
- def __rshift__(self, other):
+ def __rshift__(self, other: Any) -> "ColumnOperators":
"""implement the >> operator.
Not used by SQLAlchemy core, this is provided
@@ -506,7 +595,7 @@ class ColumnOperators(Operators):
"""
return self.operate(rshift, other)
- def concat(self, other):
+ def concat(self, other: Any) -> "ColumnOperators":
"""Implement the 'concat' operator.
In a column context, produces the clause ``a || b``,
@@ -515,7 +604,7 @@ class ColumnOperators(Operators):
"""
return self.operate(concat_op, other)
- def like(self, other, escape=None):
+ def like(self, other: Any, escape=None) -> "ColumnOperators":
r"""Implement the ``like`` operator.
In a column context, produces the expression::
@@ -540,7 +629,7 @@ class ColumnOperators(Operators):
"""
return self.operate(like_op, other, escape=escape)
- def ilike(self, other, escape=None):
+ def ilike(self, other: Any, escape=None) -> "ColumnOperators":
r"""Implement the ``ilike`` operator, e.g. case insensitive LIKE.
In a column context, produces an expression either of the form::
@@ -569,7 +658,7 @@ class ColumnOperators(Operators):
"""
return self.operate(ilike_op, other, escape=escape)
- def in_(self, other):
+ def in_(self, other: Any) -> "ColumnOperators":
"""Implement the ``in`` operator.
In a column context, produces the clause ``column IN <other>``.
@@ -658,7 +747,7 @@ class ColumnOperators(Operators):
"""
return self.operate(in_op, other)
- def not_in(self, other):
+ def not_in(self, other: Any) -> "ColumnOperators":
"""implement the ``NOT IN`` operator.
This is equivalent to using negation with
@@ -689,7 +778,7 @@ class ColumnOperators(Operators):
# deprecated 1.4; see #5429
notin_ = not_in
- def not_like(self, other, escape=None):
+ def not_like(self, other: Any, escape=None) -> "ColumnOperators":
"""implement the ``NOT LIKE`` operator.
This is equivalent to using negation with
@@ -709,7 +798,7 @@ class ColumnOperators(Operators):
# deprecated 1.4; see #5435
notlike = not_like
- def not_ilike(self, other, escape=None):
+ def not_ilike(self, other: Any, escape=None) -> "ColumnOperators":
"""implement the ``NOT ILIKE`` operator.
This is equivalent to using negation with
@@ -729,7 +818,7 @@ class ColumnOperators(Operators):
# deprecated 1.4; see #5435
notilike = not_ilike
- def is_(self, other):
+ def is_(self, other: Any) -> "ColumnOperators":
"""Implement the ``IS`` operator.
Normally, ``IS`` is generated automatically when comparing to a
@@ -742,7 +831,7 @@ class ColumnOperators(Operators):
"""
return self.operate(is_, other)
- def is_not(self, other):
+ def is_not(self, other: Any) -> "ColumnOperators":
"""Implement the ``IS NOT`` operator.
Normally, ``IS NOT`` is generated automatically when comparing to a
@@ -762,7 +851,9 @@ class ColumnOperators(Operators):
# deprecated 1.4; see #5429
isnot = is_not
- def startswith(self, other, **kwargs):
+ def startswith(
+ self, other: Any, escape=None, autoescape=False
+ ) -> "ColumnOperators":
r"""Implement the ``startswith`` operator.
Produces a LIKE expression that tests against a match for the start
@@ -839,9 +930,13 @@ class ColumnOperators(Operators):
:meth:`.ColumnOperators.like`
"""
- return self.operate(startswith_op, other, **kwargs)
+ return self.operate(
+ startswith_op, other, escape=escape, autoescape=autoescape
+ )
- def endswith(self, other, **kwargs):
+ def endswith(
+ self, other: Any, escape=None, autoescape=False
+ ) -> "ColumnOperators":
r"""Implement the 'endswith' operator.
Produces a LIKE expression that tests against a match for the end
@@ -918,9 +1013,13 @@ class ColumnOperators(Operators):
:meth:`.ColumnOperators.like`
"""
- return self.operate(endswith_op, other, **kwargs)
+ return self.operate(
+ endswith_op, other, escape=escape, autoescape=autoescape
+ )
- def contains(self, other, **kwargs):
+ def contains(
+ self, other: Any, escape=None, autoescape=False
+ ) -> "ColumnOperators":
r"""Implement the 'contains' operator.
Produces a LIKE expression that tests against a match for the middle
@@ -998,9 +1097,11 @@ class ColumnOperators(Operators):
"""
- return self.operate(contains_op, other, **kwargs)
+ return self.operate(
+ contains_op, other, escape=escape, autoescape=autoescape
+ )
- def match(self, other, **kwargs):
+ def match(self, other: Any, **kwargs) -> "ColumnOperators":
"""Implements a database-specific 'match' operator.
:meth:`_sql.ColumnOperators.match` attempts to resolve to
@@ -1024,7 +1125,7 @@ class ColumnOperators(Operators):
"""
return self.operate(match_op, other, **kwargs)
- def regexp_match(self, pattern, flags=None):
+ def regexp_match(self, pattern, flags=None) -> "ColumnOperators":
"""Implements a database-specific 'regexp match' operator.
E.g.::
@@ -1072,7 +1173,9 @@ class ColumnOperators(Operators):
"""
return self.operate(regexp_match_op, pattern, flags=flags)
- def regexp_replace(self, pattern, replacement, flags=None):
+ def regexp_replace(
+ self, pattern, replacement, flags=None
+ ) -> "ColumnOperators":
"""Implements a database-specific 'regexp replace' operator.
E.g.::
@@ -1111,20 +1214,23 @@ class ColumnOperators(Operators):
"""
return self.operate(
- regexp_replace_op, pattern, replacement=replacement, flags=flags
+ regexp_replace_op,
+ pattern,
+ replacement=replacement,
+ flags=flags,
)
- def desc(self):
+ def desc(self) -> "ColumnOperators":
"""Produce a :func:`_expression.desc` clause against the
parent object."""
return self.operate(desc_op)
- def asc(self):
+ def asc(self) -> "ColumnOperators":
"""Produce a :func:`_expression.asc` clause against the
parent object."""
return self.operate(asc_op)
- def nulls_first(self):
+ def nulls_first(self) -> "ColumnOperators":
"""Produce a :func:`_expression.nulls_first` clause against the
parent object.
@@ -1137,7 +1243,7 @@ class ColumnOperators(Operators):
# deprecated 1.4; see #5435
nullsfirst = nulls_first
- def nulls_last(self):
+ def nulls_last(self) -> "ColumnOperators":
"""Produce a :func:`_expression.nulls_last` clause against the
parent object.
@@ -1150,7 +1256,7 @@ class ColumnOperators(Operators):
# deprecated 1.4; see #5429
nullslast = nulls_last
- def collate(self, collation):
+ def collate(self, collation) -> "ColumnOperators":
"""Produce a :func:`_expression.collate` clause against
the parent object, given the collation string.
@@ -1161,7 +1267,7 @@ class ColumnOperators(Operators):
"""
return self.operate(collate, collation)
- def __radd__(self, other):
+ def __radd__(self, other: Any) -> "ColumnOperators":
"""Implement the ``+`` operator in reverse.
See :meth:`.ColumnOperators.__add__`.
@@ -1169,7 +1275,7 @@ class ColumnOperators(Operators):
"""
return self.reverse_operate(add, other)
- def __rsub__(self, other):
+ def __rsub__(self, other: Any) -> "ColumnOperators":
"""Implement the ``-`` operator in reverse.
See :meth:`.ColumnOperators.__sub__`.
@@ -1177,7 +1283,7 @@ class ColumnOperators(Operators):
"""
return self.reverse_operate(sub, other)
- def __rmul__(self, other):
+ def __rmul__(self, other: Any) -> "ColumnOperators":
"""Implement the ``*`` operator in reverse.
See :meth:`.ColumnOperators.__mul__`.
@@ -1185,7 +1291,7 @@ class ColumnOperators(Operators):
"""
return self.reverse_operate(mul, other)
- def __rmod__(self, other):
+ def __rmod__(self, other: Any) -> "ColumnOperators":
"""Implement the ``%`` operator in reverse.
See :meth:`.ColumnOperators.__mod__`.
@@ -1193,21 +1299,21 @@ class ColumnOperators(Operators):
"""
return self.reverse_operate(mod, other)
- def between(self, cleft, cright, symmetric=False):
+ def between(self, cleft, cright, symmetric=False) -> "ColumnOperators":
"""Produce a :func:`_expression.between` clause against
the parent object, given the lower and upper range.
"""
return self.operate(between_op, cleft, cright, symmetric=symmetric)
- def distinct(self):
+ def distinct(self) -> "ColumnOperators":
"""Produce a :func:`_expression.distinct` clause against the
parent object.
"""
return self.operate(distinct_op)
- def any_(self):
+ def any_(self) -> "ColumnOperators":
"""Produce an :func:`_expression.any_` clause against the
parent object.
@@ -1224,7 +1330,7 @@ class ColumnOperators(Operators):
"""
return self.operate(any_op)
- def all_(self):
+ def all_(self) -> "ColumnOperators":
"""Produce an :func:`_expression.all_` clause against the
parent object.
@@ -1242,7 +1348,7 @@ class ColumnOperators(Operators):
"""
return self.operate(all_op)
- def __add__(self, other):
+ def __add__(self, other: Any) -> "ColumnOperators":
"""Implement the ``+`` operator.
In a column context, produces the clause ``a + b``
@@ -1254,7 +1360,7 @@ class ColumnOperators(Operators):
"""
return self.operate(add, other)
- def __sub__(self, other):
+ def __sub__(self, other: Any) -> "ColumnOperators":
"""Implement the ``-`` operator.
In a column context, produces the clause ``a - b``.
@@ -1262,7 +1368,7 @@ class ColumnOperators(Operators):
"""
return self.operate(sub, other)
- def __mul__(self, other):
+ def __mul__(self, other: Any) -> "ColumnOperators":
"""Implement the ``*`` operator.
In a column context, produces the clause ``a * b``.
@@ -1270,7 +1376,7 @@ class ColumnOperators(Operators):
"""
return self.operate(mul, other)
- def __mod__(self, other):
+ def __mod__(self, other: Any) -> "ColumnOperators":
"""Implement the ``%`` operator.
In a column context, produces the clause ``a % b``.
@@ -1278,7 +1384,7 @@ class ColumnOperators(Operators):
"""
return self.operate(mod, other)
- def __truediv__(self, other):
+ def __truediv__(self, other: Any) -> "ColumnOperators":
"""Implement the ``/`` operator.
In a column context, produces the clause ``a / b``, and
@@ -1291,7 +1397,7 @@ class ColumnOperators(Operators):
"""
return self.operate(truediv, other)
- def __rtruediv__(self, other):
+ def __rtruediv__(self, other: Any) -> "ColumnOperators":
"""Implement the ``/`` operator in reverse.
See :meth:`.ColumnOperators.__truediv__`.
@@ -1299,7 +1405,7 @@ class ColumnOperators(Operators):
"""
return self.reverse_operate(truediv, other)
- def __floordiv__(self, other):
+ def __floordiv__(self, other: Any) -> "ColumnOperators":
"""Implement the ``//`` operator.
In a column context, produces the clause ``a / b``,
@@ -1311,7 +1417,7 @@ class ColumnOperators(Operators):
"""
return self.operate(floordiv, other)
- def __rfloordiv__(self, other):
+ def __rfloordiv__(self, other: Any) -> "ColumnOperators":
"""Implement the ``//`` operator in reverse.
See :meth:`.ColumnOperators.__floordiv__`.
@@ -1324,6 +1430,10 @@ _commutative = {eq, ne, add, mul}
_comparison = {eq, ne, lt, gt, ge, le}
+def _operator_fn(fn):
+ return cast(OperatorType, fn)
+
+
def commutative_op(fn):
_commutative.add(fn)
return fn
@@ -1351,6 +1461,7 @@ def exists():
raise NotImplementedError()
+@_operator_fn
def is_true(a):
raise NotImplementedError()
@@ -1359,6 +1470,7 @@ def is_true(a):
istrue = is_true
+@_operator_fn
def is_false(a):
raise NotImplementedError()
@@ -1368,11 +1480,13 @@ isfalse = is_false
@comparison_op
+@_operator_fn
def is_distinct_from(a, b):
return a.is_distinct_from(b)
@comparison_op
+@_operator_fn
def is_not_distinct_from(a, b):
return a.is_not_distinct_from(b)
@@ -1382,11 +1496,13 @@ isnot_distinct_from = is_not_distinct_from
@comparison_op
+@_operator_fn
def is_(a, b):
return a.is_(b)
@comparison_op
+@_operator_fn
def is_not(a, b):
return a.is_not(b)
@@ -1395,20 +1511,24 @@ def is_not(a, b):
isnot = is_not
+@_operator_fn
def collate(a, b):
return a.collate(b)
+@_operator_fn
def op(a, opstring, b):
return a.op(opstring)(b)
@comparison_op
+@_operator_fn
def like_op(a, b, escape=None):
return a.like(b, escape=escape)
@comparison_op
+@_operator_fn
def not_like_op(a, b, escape=None):
return a.notlike(b, escape=escape)
@@ -1418,11 +1538,13 @@ notlike_op = not_like_op
@comparison_op
+@_operator_fn
def ilike_op(a, b, escape=None):
return a.ilike(b, escape=escape)
@comparison_op
+@_operator_fn
def not_ilike_op(a, b, escape=None):
return a.not_ilike(b, escape=escape)
@@ -1432,11 +1554,13 @@ notilike_op = not_ilike_op
@comparison_op
+@_operator_fn
def between_op(a, b, c, symmetric=False):
return a.between(b, c, symmetric=symmetric)
@comparison_op
+@_operator_fn
def not_between_op(a, b, c, symmetric=False):
return ~a.between(b, c, symmetric=symmetric)
@@ -1446,11 +1570,13 @@ notbetween_op = not_between_op
@comparison_op
+@_operator_fn
def in_op(a, b):
return a.in_(b)
@comparison_op
+@_operator_fn
def not_in_op(a, b):
return a.not_in(b)
@@ -1459,19 +1585,22 @@ def not_in_op(a, b):
notin_op = not_in_op
+@_operator_fn
def distinct_op(a):
return a.distinct()
+@_operator_fn
def any_op(a):
return a.any_()
+@_operator_fn
def all_op(a):
return a.all_()
-def _escaped_like_impl(fn, other, escape, autoescape):
+def _escaped_like_impl(fn, other: Any, escape, autoescape):
if autoescape:
if autoescape is not True:
util.warn(
@@ -1492,11 +1621,13 @@ def _escaped_like_impl(fn, other, escape, autoescape):
@comparison_op
+@_operator_fn
def startswith_op(a, b, escape=None, autoescape=False):
return _escaped_like_impl(a.startswith, b, escape, autoescape)
@comparison_op
+@_operator_fn
def not_startswith_op(a, b, escape=None, autoescape=False):
return ~_escaped_like_impl(a.startswith, b, escape, autoescape)
@@ -1506,11 +1637,13 @@ notstartswith_op = not_startswith_op
@comparison_op
+@_operator_fn
def endswith_op(a, b, escape=None, autoescape=False):
return _escaped_like_impl(a.endswith, b, escape, autoescape)
@comparison_op
+@_operator_fn
def not_endswith_op(a, b, escape=None, autoescape=False):
return ~_escaped_like_impl(a.endswith, b, escape, autoescape)
@@ -1520,11 +1653,13 @@ notendswith_op = not_endswith_op
@comparison_op
+@_operator_fn
def contains_op(a, b, escape=None, autoescape=False):
return _escaped_like_impl(a.contains, b, escape, autoescape)
@comparison_op
+@_operator_fn
def not_contains_op(a, b, escape=None, autoescape=False):
return ~_escaped_like_impl(a.contains, b, escape, autoescape)
@@ -1534,25 +1669,30 @@ notcontains_op = not_contains_op
@comparison_op
+@_operator_fn
def match_op(a, b, **kw):
return a.match(b, **kw)
@comparison_op
+@_operator_fn
def regexp_match_op(a, b, flags=None):
return a.regexp_match(b, flags=flags)
@comparison_op
+@_operator_fn
def not_regexp_match_op(a, b, flags=None):
return ~a.regexp_match(b, flags=flags)
+@_operator_fn
def regexp_replace_op(a, b, replacement, flags=None):
return a.regexp_replace(b, replacement=replacement, flags=flags)
@comparison_op
+@_operator_fn
def not_match_op(a, b, **kw):
return ~a.match(b, **kw)
@@ -1561,26 +1701,32 @@ def not_match_op(a, b, **kw):
notmatch_op = not_match_op
+@_operator_fn
def comma_op(a, b):
raise NotImplementedError()
+@_operator_fn
def filter_op(a, b):
raise NotImplementedError()
+@_operator_fn
def concat_op(a, b):
return a.concat(b)
+@_operator_fn
def desc_op(a):
return a.desc()
+@_operator_fn
def asc_op(a):
return a.asc()
+@_operator_fn
def nulls_first_op(a):
return a.nulls_first()
@@ -1589,6 +1735,7 @@ def nulls_first_op(a):
nullsfirst_op = nulls_first_op
+@_operator_fn
def nulls_last_op(a):
return a.nulls_last()
@@ -1597,10 +1744,12 @@ def nulls_last_op(a):
nullslast_op = nulls_last_op
+@_operator_fn
def json_getitem_op(a, b):
raise NotImplementedError()
+@_operator_fn
def json_path_getitem_op(a, b):
raise NotImplementedError()
diff --git a/lib/sqlalchemy/sql/roles.py b/lib/sqlalchemy/sql/roles.py
index 9172c2dc9..787a1c25e 100644
--- a/lib/sqlalchemy/sql/roles.py
+++ b/lib/sqlalchemy/sql/roles.py
@@ -71,7 +71,13 @@ class ColumnListRole(SQLRole):
__slots__ = ()
-class TruncatedLabelRole(SQLRole):
+class StringRole(SQLRole):
+ """mixin indicating a role that results in strings"""
+
+ __slots__ = ()
+
+
+class TruncatedLabelRole(StringRole, SQLRole):
__slots__ = ()
_role_name = "String SQL identifier"
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index 885cb2754..938d2c34a 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -29,6 +29,14 @@ as components in SQL expressions.
"""
import collections
+import typing
+from typing import Any
+from typing import MutableMapping
+from typing import Optional
+from typing import overload
+from typing import Type
+from typing import TypeVar
+from typing import Union
from . import coercions
from . import ddl
@@ -52,7 +60,13 @@ from .. import event
from .. import exc
from .. import inspection
from .. import util
+from ..util.typing import Literal
+if typing.TYPE_CHECKING:
+ from .type_api import TypeEngine
+
+_T = TypeVar("_T", bound="Any")
+_ServerDefaultType = Union["FetchedValue", str, TextClause, ColumnElement]
RETAIN_SCHEMA = util.symbol("retain_schema")
@@ -1086,13 +1100,109 @@ class Table(DialectKWArgs, SchemaItem, TableClause):
return self._schema_item_copy(table)
-class Column(DialectKWArgs, SchemaItem, ColumnClause):
+class Column(DialectKWArgs, SchemaItem, ColumnClause[_T]):
"""Represents a column in a database table."""
__visit_name__ = "column"
inherit_cache = True
+ @overload
+ def __init__(
+ self: "Column[None]",
+ __name: str,
+ *args: SchemaEventTarget,
+ autoincrement: Union[bool, Literal["auto", "ignore_fk"]] = ...,
+ default: Optional[Any] = ...,
+ doc: Optional[str] = ...,
+ key: Optional[str] = ...,
+ index: Optional[bool] = ...,
+ info: MutableMapping[Any, Any] = ...,
+ nullable: bool = ...,
+ onupdate: Optional[Any] = ...,
+ primary_key: bool = ...,
+ server_default: Optional[_ServerDefaultType] = ...,
+ server_onupdate: Optional["FetchedValue"] = ...,
+ quote: Optional[bool] = ...,
+ unique: Optional[bool] = ...,
+ system: bool = ...,
+ comment: Optional[str] = ...,
+ **kwargs: Any,
+ ) -> None:
+ ...
+
+ @overload
+ def __init__(
+ self: "Column[None]",
+ *args: SchemaEventTarget,
+ autoincrement: Union[bool, Literal["auto", "ignore_fk"]] = ...,
+ default: Optional[Any] = ...,
+ doc: Optional[str] = ...,
+ key: Optional[str] = ...,
+ index: Optional[bool] = ...,
+ info: MutableMapping[Any, Any] = ...,
+ nullable: bool = ...,
+ onupdate: Optional[Any] = ...,
+ primary_key: bool = ...,
+ server_default: Optional[_ServerDefaultType] = ...,
+ server_onupdate: Optional["FetchedValue"] = ...,
+ quote: Optional[bool] = ...,
+ unique: Optional[bool] = ...,
+ system: bool = ...,
+ comment: Optional[str] = ...,
+ **kwargs: Any,
+ ) -> None:
+ ...
+
+ @overload
+ def __init__(
+ self,
+ __name: str,
+ __type: Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"],
+ *args: SchemaEventTarget,
+ autoincrement: Union[bool, Literal["auto", "ignore_fk"]] = ...,
+ default: Optional[Any] = ...,
+ doc: Optional[str] = ...,
+ key: Optional[str] = ...,
+ index: Optional[bool] = ...,
+ info: MutableMapping[Any, Any] = ...,
+ nullable: bool = ...,
+ onupdate: Optional[Any] = ...,
+ primary_key: bool = ...,
+ server_default: Optional[_ServerDefaultType] = ...,
+ server_onupdate: Optional["FetchedValue"] = ...,
+ quote: Optional[bool] = ...,
+ unique: Optional[bool] = ...,
+ system: bool = ...,
+ comment: Optional[str] = ...,
+ **kwargs: Any,
+ ) -> None:
+ ...
+
+ @overload
+ def __init__(
+ self,
+ __type: Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"],
+ *args: SchemaEventTarget,
+ autoincrement: Union[bool, Literal["auto", "ignore_fk"]] = ...,
+ default: Optional[Any] = ...,
+ doc: Optional[str] = ...,
+ key: Optional[str] = ...,
+ index: Optional[bool] = ...,
+ info: MutableMapping[Any, Any] = ...,
+ nullable: bool = ...,
+ onupdate: Optional[Any] = ...,
+ primary_key: bool = ...,
+ server_default: Optional[_ServerDefaultType] = ...,
+ server_onupdate: Optional["FetchedValue"] = ...,
+ quote: Optional[bool] = ...,
+ unique: Optional[bool] = ...,
+ system: bool = ...,
+ comment: Optional[str] = ...,
+ **kwargs: Any,
+ ) -> None:
+ ...
+
def __init__(self, *args, **kwargs):
r"""
Construct a new ``Column`` object.
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index e674c4b74..fd1abd71b 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -48,7 +48,6 @@ from .base import Immutable
from .base import prefix_anon_map
from .coercions import _document_text_coercion
from .elements import _anonymous_label
-from .elements import and_
from .elements import BindParameter
from .elements import BooleanClauseList
from .elements import ClauseElement
@@ -64,6 +63,9 @@ from .. import exc
from .. import util
+and_ = BooleanClauseList.and_
+
+
class _OffsetLimitParam(BindParameter):
inherit_cache = True
@@ -472,7 +474,7 @@ class FromClause(roles.AnonymizedFromClauseRole, Selectable):
method which allows for arbitrary column lists.
"""
- return Select._create(self)
+ return Select(self)
def join(self, right, onclause=None, isouter=False, full=False):
"""Return a :class:`_expression.Join` from this
@@ -990,86 +992,6 @@ class Join(roles.DMLTableRole, FromClause):
self.isouter = isouter
self.full = full
- @classmethod
- def _create_outerjoin(cls, left, right, onclause=None, full=False):
- """Return an ``OUTER JOIN`` clause element.
-
- The returned object is an instance of :class:`_expression.Join`.
-
- Similar functionality is also available via the
- :meth:`_expression.FromClause.outerjoin` method on any
- :class:`_expression.FromClause`.
-
- :param left: The left side of the join.
-
- :param right: The right side of the join.
-
- :param onclause: Optional criterion for the ``ON`` clause, is
- derived from foreign key relationships established between
- left and right otherwise.
-
- To chain joins together, use the :meth:`_expression.FromClause.join`
- or
- :meth:`_expression.FromClause.outerjoin` methods on the resulting
- :class:`_expression.Join` object.
-
- """
- return cls(left, right, onclause, isouter=True, full=full)
-
- @classmethod
- def _create_join(
- cls, left, right, onclause=None, isouter=False, full=False
- ):
- """Produce a :class:`_expression.Join` object, given two
- :class:`_expression.FromClause`
- expressions.
-
- E.g.::
-
- j = join(user_table, address_table,
- user_table.c.id == address_table.c.user_id)
- stmt = select(user_table).select_from(j)
-
- would emit SQL along the lines of::
-
- SELECT user.id, user.name FROM user
- JOIN address ON user.id = address.user_id
-
- Similar functionality is available given any
- :class:`_expression.FromClause` object (e.g. such as a
- :class:`_schema.Table`) using
- the :meth:`_expression.FromClause.join` method.
-
- :param left: The left side of the join.
-
- :param right: the right side of the join; this is any
- :class:`_expression.FromClause` object such as a
- :class:`_schema.Table` object, and
- may also be a selectable-compatible object such as an ORM-mapped
- class.
-
- :param onclause: a SQL expression representing the ON clause of the
- join. If left at ``None``, :meth:`_expression.FromClause.join`
- will attempt to
- join the two tables based on a foreign key relationship.
-
- :param isouter: if True, render a LEFT OUTER JOIN, instead of JOIN.
-
- :param full: if True, render a FULL OUTER JOIN, instead of JOIN.
-
- .. versionadded:: 1.1
-
- .. seealso::
-
- :meth:`_expression.FromClause.join` - method form,
- based on a given left side.
-
- :class:`_expression.Join` - the type of object produced.
-
- """
-
- return cls(left, right, onclause, isouter, full)
-
@property
def description(self):
return "Join object on %s(%d) and %s(%d)" % (
@@ -1161,24 +1083,7 @@ class Join(roles.DMLTableRole, FromClause):
):
"""Create a join condition between two tables or selectables.
- e.g.::
-
- join_condition(tablea, tableb)
-
- would produce an expression along the lines of::
-
- tablea.c.id==tableb.c.tablea_id
-
- The join is determined based on the foreign key relationships
- between the two selectables. If there are multiple ways
- to join, or no way to join, an error is raised.
-
- :param a_subset: An optional expression that is a sub-component
- of ``a``. An attempt will be made to join to just this sub-component
- first before looking at the full ``a`` construct, and if found
- will be successful even if there are other ways to join to ``a``.
- This allows the "right side" of a join to be passed thereby
- providing a "natural join".
+ See sqlalchemy.sql.util.join_condition() for full docs.
"""
constraints = cls._joincond_scan_left_right(
@@ -1331,7 +1236,7 @@ class Join(roles.DMLTableRole, FromClause):
FROM table_a JOIN table_b ON table_a.id = table_b.a_id
"""
- return Select._create(self.left, self.right).select_from(self)
+ return Select(self.left, self.right).select_from(self)
@util.preload_module("sqlalchemy.sql.util")
def _anonymous_fromclause(self, name=None, flat=False):
@@ -1503,51 +1408,6 @@ class Alias(roles.DMLTableRole, AliasedReturnsRows):
@classmethod
def _factory(cls, selectable, name=None, flat=False):
- """Return an :class:`_expression.Alias` object.
-
- An :class:`_expression.Alias` represents any
- :class:`_expression.FromClause`
- with an alternate name assigned within SQL, typically using the ``AS``
- clause when generated, e.g. ``SELECT * FROM table AS aliasname``.
-
- Similar functionality is available via the
- :meth:`_expression.FromClause.alias`
- method available on all :class:`_expression.FromClause` subclasses.
- In terms of
- a SELECT object as generated from the :func:`_expression.select`
- function, the :meth:`_expression.SelectBase.alias` method returns an
- :class:`_expression.Alias` or similar object which represents a named,
- parenthesized subquery.
-
- When an :class:`_expression.Alias` is created from a
- :class:`_schema.Table` object,
- this has the effect of the table being rendered
- as ``tablename AS aliasname`` in a SELECT statement.
-
- For :func:`_expression.select` objects, the effect is that of
- creating a named subquery, i.e. ``(select ...) AS aliasname``.
-
- The ``name`` parameter is optional, and provides the name
- to use in the rendered SQL. If blank, an "anonymous" name
- will be deterministically generated at compile time.
- Deterministic means the name is guaranteed to be unique against
- other constructs used in the same statement, and will also be the
- same name for each successive compilation of the same statement
- object.
-
- :param selectable: any :class:`_expression.FromClause` subclass,
- such as a table, select statement, etc.
-
- :param name: string name to be assigned as the alias.
- If ``None``, a name will be deterministically generated
- at compile time.
-
- :param flat: Will be passed through to if the given selectable
- is an instance of :class:`_expression.Join` - see
- :meth:`_expression.Join.alias`
- for details.
-
- """
return coercions.expect(
roles.FromClauseRole, selectable, allow_select=True
).alias(name=name, flat=flat)
@@ -1724,25 +1584,6 @@ class Lateral(AliasedReturnsRows):
@classmethod
def _factory(cls, selectable, name=None):
- """Return a :class:`_expression.Lateral` object.
-
- :class:`_expression.Lateral` is an :class:`_expression.Alias`
- subclass that represents
- a subquery with the LATERAL keyword applied to it.
-
- The special behavior of a LATERAL subquery is that it appears in the
- FROM clause of an enclosing SELECT, but may correlate to other
- FROM clauses of that SELECT. It is a special case of subquery
- only supported by a small number of backends, currently more recent
- PostgreSQL versions.
-
- .. versionadded:: 1.1
-
- .. seealso::
-
- :ref:`lateral_selects` - overview of usage.
-
- """
return coercions.expect(
roles.FromClauseRole, selectable, explicit_subquery=True
).lateral(name=name)
@@ -1773,48 +1614,6 @@ class TableSample(AliasedReturnsRows):
@classmethod
def _factory(cls, selectable, sampling, name=None, seed=None):
- """Return a :class:`_expression.TableSample` object.
-
- :class:`_expression.TableSample` is an :class:`_expression.Alias`
- subclass that represents
- a table with the TABLESAMPLE clause applied to it.
- :func:`_expression.tablesample`
- is also available from the :class:`_expression.FromClause`
- class via the
- :meth:`_expression.FromClause.tablesample` method.
-
- The TABLESAMPLE clause allows selecting a randomly selected approximate
- percentage of rows from a table. It supports multiple sampling methods,
- most commonly BERNOULLI and SYSTEM.
-
- e.g.::
-
- from sqlalchemy import func
-
- selectable = people.tablesample(
- func.bernoulli(1),
- name='alias',
- seed=func.random())
- stmt = select(selectable.c.people_id)
-
- Assuming ``people`` with a column ``people_id``, the above
- statement would render as::
-
- SELECT alias.people_id FROM
- people AS alias TABLESAMPLE bernoulli(:bernoulli_1)
- REPEATABLE (random())
-
- .. versionadded:: 1.1
-
- :param sampling: a ``float`` percentage between 0 and 100 or
- :class:`_functions.Function`.
-
- :param name: optional alias name
-
- :param seed: any real-valued SQL expression. When specified, the
- REPEATABLE sub-clause is also rendered.
-
- """
return coercions.expect(roles.FromClauseRole, selectable).tablesample(
sampling, name=name, seed=seed
)
@@ -2493,28 +2292,6 @@ class TableClause(roles.DMLTableRole, Immutable, FromClause):
"""No PK or default support so no autoincrement column."""
def __init__(self, name, *columns, **kw):
- """Produce a new :class:`_expression.TableClause`.
-
- The object returned is an instance of
- :class:`_expression.TableClause`, which
- represents the "syntactical" portion of the schema-level
- :class:`_schema.Table` object.
- It may be used to construct lightweight table constructs.
-
- .. versionchanged:: 1.0.0 :func:`_expression.table` can now
- be imported from the plain ``sqlalchemy`` namespace like any
- other SQL element.
-
-
- :param name: Name of the table.
-
- :param columns: A collection of :func:`_expression.column` constructs.
-
- :param schema: The schema name for this table.
-
- .. versionadded:: 1.3.18 :func:`_expression.table` can now
- accept a ``schema`` argument.
- """
super(TableClause, self).__init__()
self.name = name
self._columns = DedupeColumnCollection()
@@ -2697,41 +2474,6 @@ class Values(Generative, FromClause):
]
def __init__(self, *columns, name=None, literal_binds=False):
- r"""Construct a :class:`_expression.Values` construct.
-
- The column expressions and the actual data for
- :class:`_expression.Values` are given in two separate steps. The
- constructor receives the column expressions typically as
- :func:`_expression.column` constructs,
- and the data is then passed via the
- :meth:`_expression.Values.data` method as a list,
- which can be called multiple
- times to add more data, e.g.::
-
- from sqlalchemy import column
- from sqlalchemy import values
-
- value_expr = values(
- column('id', Integer),
- column('name', String),
- name="my_values"
- ).data(
- [(1, 'name1'), (2, 'name2'), (3, 'name3')]
- )
-
- :param \*columns: column expressions, typically composed using
- :func:`_expression.column` objects.
-
- :param name: the name for this VALUES construct. If omitted, the
- VALUES construct will be unnamed in a SQL expression. Different
- backends may have different requirements here.
-
- :param literal_binds: Defaults to False. Whether or not to render
- the data values inline in the SQL output, rather than using bound
- parameters.
-
- """
-
super(Values, self).__init__()
self._column_args = columns
self.name = name
@@ -3708,91 +3450,26 @@ class CompoundSelect(HasCompileState, GenerativeSelect):
@classmethod
def _create_union(cls, *selects, **kwargs):
- r"""Return a ``UNION`` of multiple selectables.
-
- The returned object is an instance of
- :class:`_expression.CompoundSelect`.
-
- A similar :func:`union()` method is available on all
- :class:`_expression.FromClause` subclasses.
-
- :param \*selects:
- a list of :class:`_expression.Select` instances.
-
- :param \**kwargs:
- available keyword arguments are the same as those of
- :func:`select`.
-
- """
return CompoundSelect(CompoundSelect.UNION, *selects, **kwargs)
@classmethod
def _create_union_all(cls, *selects):
- r"""Return a ``UNION ALL`` of multiple selectables.
-
- The returned object is an instance of
- :class:`_expression.CompoundSelect`.
-
- A similar :func:`union_all()` method is available on all
- :class:`_expression.FromClause` subclasses.
-
- :param \*selects:
- a list of :class:`_expression.Select` instances.
-
- """
return CompoundSelect(CompoundSelect.UNION_ALL, *selects)
@classmethod
def _create_except(cls, *selects):
- r"""Return an ``EXCEPT`` of multiple selectables.
-
- The returned object is an instance of
- :class:`_expression.CompoundSelect`.
-
- :param \*selects:
- a list of :class:`_expression.Select` instances.
-
- """
return CompoundSelect(CompoundSelect.EXCEPT, *selects)
@classmethod
def _create_except_all(cls, *selects):
- r"""Return an ``EXCEPT ALL`` of multiple selectables.
-
- The returned object is an instance of
- :class:`_expression.CompoundSelect`.
-
- :param \*selects:
- a list of :class:`_expression.Select` instances.
-
- """
return CompoundSelect(CompoundSelect.EXCEPT_ALL, *selects)
@classmethod
def _create_intersect(cls, *selects):
- r"""Return an ``INTERSECT`` of multiple selectables.
-
- The returned object is an instance of
- :class:`_expression.CompoundSelect`.
-
- :param \*selects:
- a list of :class:`_expression.Select` instances.
-
- """
return CompoundSelect(CompoundSelect.INTERSECT, *selects)
@classmethod
def _create_intersect_all(cls, *selects):
- r"""Return an ``INTERSECT ALL`` of multiple selectables.
-
- The returned object is an instance of
- :class:`_expression.CompoundSelect`.
-
- :param \*selects:
- a list of :class:`_expression.Select` instances.
-
-
- """
return CompoundSelect(CompoundSelect.INTERSECT_ALL, *selects)
def _scalar_type(self):
@@ -4413,47 +4090,26 @@ class Select(
]
@classmethod
- def _create(
- cls, *entities: Union[roles.ColumnsClauseRole, Type]
- ) -> "Select":
- r"""Construct a new :class:`_expression.Select`.
+ def _create_raw_select(cls, **kw) -> "Select":
+ """Create a :class:`.Select` using raw ``__new__`` with no coercions.
+ Used internally to build up :class:`.Select` constructs with
+ pre-established state.
- .. versionadded:: 1.4 - The :func:`_sql.select` function now accepts
- column arguments positionally. The top-level :func:`_sql.select`
- function will automatically use the 1.x or 2.x style API based on
- the incoming arguments; using :func:`_future.select` from the
- ``sqlalchemy.future`` module will enforce that only the 2.x style
- constructor is used.
+ """
- Similar functionality is also available via the
- :meth:`_expression.FromClause.select` method on any
- :class:`_expression.FromClause`.
+ stmt = Select.__new__(Select)
+ stmt.__dict__.update(kw)
+ return stmt
- .. seealso::
+ def __init__(self, *entities: Union[roles.ColumnsClauseRole, Type]):
+ r"""Construct a new :class:`_expression.Select`.
- :ref:`coretutorial_selecting` - Core Tutorial description of
- :func:`_expression.select`.
-
- :param \*entities:
- Entities to SELECT from. For Core usage, this is typically a series
- of :class:`_expression.ColumnElement` and / or
- :class:`_expression.FromClause`
- objects which will form the columns clause of the resulting
- statement. For those objects that are instances of
- :class:`_expression.FromClause` (typically :class:`_schema.Table`
- or :class:`_expression.Alias`
- objects), the :attr:`_expression.FromClause.c`
- collection is extracted
- to form a collection of :class:`_expression.ColumnElement` objects.
-
- This parameter will also accept :class:`_expression.TextClause`
- constructs as
- given, as well as ORM-mapped classes.
+ The public constructor for :class:`_expression.Select` is the
+ :func:`_sql.select` function.
"""
- self = cls.__new__(cls)
self._raw_columns = [
coercions.expect(
roles.ColumnsClauseRole, ent, apply_propagate_attrs=self
@@ -4463,24 +4119,6 @@ class Select(
GenerativeSelect.__init__(self)
- return self
-
- @classmethod
- def _create_raw_select(cls, **kw) -> "Select":
- """Create a :class:`.Select` using raw ``__new__`` with no coercions.
-
- Used internally to build up :class:`.Select` constructs with
- pre-established state.
-
- """
-
- stmt = Select.__new__(Select)
- stmt.__dict__.update(kw)
- return stmt
-
- def __init__(self):
- raise NotImplementedError()
-
def _scalar_type(self):
elem = self._raw_columns[0]
cols = list(elem._select_iterable)
@@ -5787,52 +5425,16 @@ class Exists(UnaryExpression):
"""
- _from_objects = []
+ _from_objects = ()
inherit_cache = True
- def __init__(self, *args, **kwargs):
- """Construct a new :class:`_expression.Exists` construct.
-
- The :func:`_sql.exists` can be invoked by itself to produce an
- :class:`_sql.Exists` construct, which will accept simple WHERE
- criteria::
-
- exists_criteria = exists().where(table1.c.col1 == table2.c.col2)
-
- However, for greater flexibility in constructing the SELECT, an
- existing :class:`_sql.Select` construct may be converted to an
- :class:`_sql.Exists`, most conveniently by making use of the
- :meth:`_sql.SelectBase.exists` method::
-
- exists_criteria = (
- select(table2.c.col2).
- where(table1.c.col1 == table2.c.col2).
- exists()
- )
-
- The EXISTS criteria is then used inside of an enclosing SELECT::
-
- stmt = select(table1.c.col1).where(exists_criteria)
-
- The above statement will then be of the form::
-
- SELECT col1 FROM table1 WHERE EXISTS
- (SELECT table2.col2 FROM table2 WHERE table2.col2 = table1.col1)
-
- .. seealso::
-
- :ref:`tutorial_exists` - in the :term:`2.0 style` tutorial.
-
- :meth:`_sql.SelectBase.exists` - method to transform a ``SELECT`` to an
- ``EXISTS`` clause.
-
- """ # noqa E501
- if args and isinstance(args[0], (SelectBase, ScalarSelect)):
- s = args[0]
+ def __init__(self, __argument=None):
+ if __argument is None:
+ s = Select(literal_column("*")).scalar_subquery()
+ elif isinstance(__argument, (SelectBase, ScalarSelect)):
+ s = __argument
else:
- if not args:
- args = (literal_column("*"),)
- s = Select._create(*args, **kwargs).scalar_subquery()
+ s = Select(__argument).scalar_subquery()
UnaryExpression.__init__(
self,
@@ -5865,7 +5467,7 @@ class Exists(UnaryExpression):
""" # noqa
- return Select._create(self)
+ return Select(self)
def correlate(self, *fromclause):
"""Apply correlation to the subquery noted by this :class:`_sql.Exists`.
diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py
index 93ff53663..81434fbb9 100644
--- a/lib/sqlalchemy/sql/sqltypes.py
+++ b/lib/sqlalchemy/sql/sqltypes.py
@@ -8,12 +8,18 @@
"""SQL specific types.
"""
-
import collections.abc as collections_abc
import datetime as dt
import decimal
+import enum
import json
import pickle
+from typing import Any
+from typing import Sequence
+from typing import Text as typing_Text
+from typing import Tuple
+from typing import TypeVar
+from typing import Union
from . import coercions
from . import elements
@@ -43,6 +49,9 @@ from ..util import langhelpers
from ..util import OrderedDict
+_T = TypeVar("_T", bound="Any")
+
+
class _LookupExpressionAdapter:
"""Mixin expression adaptations based on lookup tables.
@@ -121,7 +130,7 @@ class Indexable:
comparator_factory = Comparator
-class String(Concatenable, TypeEngine):
+class String(Concatenable, TypeEngine[typing_Text]):
"""The base for all string and character types.
@@ -136,7 +145,10 @@ class String(Concatenable, TypeEngine):
__visit_name__ = "string"
def __init__(
- self,
+ # note pylance appears to require the "self" type in a constructor
+ # for the _T type to be correctly recognized when we send the
+ # class as the argument, e.g. `column("somecol", String)`
+ self: "String",
length=None,
collation=None,
):
@@ -289,7 +301,7 @@ class UnicodeText(Text):
super(UnicodeText, self).__init__(length=length, **kwargs)
-class Integer(_LookupExpressionAdapter, TypeEngine):
+class Integer(_LookupExpressionAdapter, TypeEngine[int]):
"""A type for ``int`` integers."""
@@ -351,7 +363,9 @@ class BigInteger(Integer):
__visit_name__ = "big_integer"
-class Numeric(_LookupExpressionAdapter, TypeEngine):
+class Numeric(
+ _LookupExpressionAdapter, TypeEngine[Union[decimal.Decimal, float]]
+):
"""A type for fixed precision numbers, such as ``NUMERIC`` or ``DECIMAL``.
@@ -396,7 +410,7 @@ class Numeric(_LookupExpressionAdapter, TypeEngine):
_default_decimal_return_scale = 10
def __init__(
- self,
+ self: "Numeric",
precision=None,
scale=None,
decimal_return_scale=None,
@@ -544,7 +558,10 @@ class Float(Numeric):
scale = None
def __init__(
- self, precision=None, asdecimal=False, decimal_return_scale=None
+ self: "Float",
+ precision=None,
+ asdecimal=False,
+ decimal_return_scale=None,
):
r"""
Construct a Float.
@@ -583,7 +600,7 @@ class Float(Numeric):
return None
-class DateTime(_LookupExpressionAdapter, TypeEngine):
+class DateTime(_LookupExpressionAdapter, TypeEngine[dt.datetime]):
"""A type for ``datetime.datetime()`` objects.
@@ -645,7 +662,7 @@ class DateTime(_LookupExpressionAdapter, TypeEngine):
}
-class Date(_LookupExpressionAdapter, TypeEngine):
+class Date(_LookupExpressionAdapter, TypeEngine[dt.date]):
"""A type for ``datetime.date()`` objects."""
@@ -683,7 +700,7 @@ class Date(_LookupExpressionAdapter, TypeEngine):
}
-class Time(_LookupExpressionAdapter, TypeEngine):
+class Time(_LookupExpressionAdapter, TypeEngine[dt.time]):
"""A type for ``datetime.time()`` objects."""
@@ -717,7 +734,7 @@ class Time(_LookupExpressionAdapter, TypeEngine):
}
-class _Binary(TypeEngine):
+class _Binary(TypeEngine[bytes]):
"""Define base behavior for binary types."""
@@ -1019,7 +1036,7 @@ class SchemaType(SchemaEventTarget):
return _we_are_the_impl(variant_mapping["_default"])
-class Enum(Emulated, String, SchemaType):
+class Enum(Emulated, String, TypeEngine[Union[str, enum.Enum]], SchemaType):
"""Generic Enum Type.
The :class:`.Enum` type provides a set of possible string values
@@ -1496,7 +1513,7 @@ class Enum(Emulated, String, SchemaType):
return super(Enum, self).python_type
-class PickleType(TypeDecorator):
+class PickleType(TypeDecorator[object]):
"""Holds Python objects, which are serialized using pickle.
PickleType builds upon the Binary type to apply Python's
@@ -1597,7 +1614,7 @@ class PickleType(TypeDecorator):
return x == y
-class Boolean(Emulated, TypeEngine, SchemaType):
+class Boolean(Emulated, TypeEngine[bool], SchemaType):
"""A bool datatype.
@@ -1621,7 +1638,10 @@ class Boolean(Emulated, TypeEngine, SchemaType):
native = True
def __init__(
- self, create_constraint=False, name=None, _create_events=True
+ self: "Boolean",
+ create_constraint=False,
+ name=None,
+ _create_events=True,
):
"""Construct a Boolean.
@@ -1723,7 +1743,7 @@ class Boolean(Emulated, TypeEngine, SchemaType):
return processors.int_to_boolean
-class _AbstractInterval(_LookupExpressionAdapter, TypeEngine):
+class _AbstractInterval(_LookupExpressionAdapter, TypeEngine[dt.timedelta]):
@util.memoized_property
def _expression_adaptations(self):
# Based on https://www.postgresql.org/docs/current/\
@@ -1841,7 +1861,7 @@ class Interval(Emulated, _AbstractInterval, TypeDecorator):
return process
-class JSON(Indexable, TypeEngine):
+class JSON(Indexable, TypeEngine[Any]):
"""Represent a SQL JSON type.
.. note:: :class:`_types.JSON`
@@ -2399,7 +2419,9 @@ class JSON(Indexable, TypeEngine):
return process
-class ARRAY(SchemaEventTarget, Indexable, Concatenable, TypeEngine):
+class ARRAY(
+ SchemaEventTarget, Indexable, Concatenable, TypeEngine[Sequence[Any]]
+):
"""Represent a SQL Array type.
.. note:: This type serves as the basis for all ARRAY operations.
@@ -2700,7 +2722,7 @@ class ARRAY(SchemaEventTarget, Indexable, Concatenable, TypeEngine):
self.item_type._set_parent_with_dispatch(parent)
-class TupleType(TypeEngine):
+class TupleType(TypeEngine[Tuple[Any]]):
"""represent the composite type of a Tuple."""
_is_tuple_type = True
diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py
index 7981100a4..75eb1b8c5 100644
--- a/lib/sqlalchemy/sql/type_api.py
+++ b/lib/sqlalchemy/sql/type_api.py
@@ -10,30 +10,48 @@
"""
import typing
+from typing import Any
+from typing import Generic
+from typing import Tuple
+from typing import Type
+from typing import TypeVar
+from typing import Union
-from . import operators
from .base import SchemaEventTarget
from .cache_key import NO_CACHE
+from .operators import ColumnOperators
from .visitors import Traversible
from .. import exc
from .. import util
# these are back-assigned by sqltypes.
-BOOLEANTYPE = None
-INTEGERTYPE = None
-NULLTYPE = None
-STRINGTYPE = None
-MATCHTYPE = None
-INDEXABLE = None
-TABLEVALUE = None
-_resolve_value_to_type = None
-
+if not typing.TYPE_CHECKING:
+ BOOLEANTYPE = None
+ INTEGERTYPE = None
+ NULLTYPE = None
+ STRINGTYPE = None
+ MATCHTYPE = None
+ INDEXABLE = None
+ TABLEVALUE = None
+ _resolve_value_to_type = None
+
+if typing.TYPE_CHECKING:
+ from .elements import ColumnElement
+ from .operators import OperatorType
+ from .sqltypes import _resolve_value_to_type
+ from .sqltypes import Boolean as BOOLEANTYPE # noqa
+ from .sqltypes import Indexable as INDEXABLE # noqa
+ from .sqltypes import MatchType as MATCHTYPE # noqa
+ from .sqltypes import NULLTYPE
+
+_T = TypeVar("_T", bound=Any)
+_CT = TypeVar("_CT", bound=Any)
# replace with pep-673 when applicable
SelfTypeEngine = typing.TypeVar("SelfTypeEngine", bound="TypeEngine")
-class TypeEngine(Traversible):
+class TypeEngine(Traversible, Generic[_T]):
"""The ultimate base class for all SQL datatypes.
Common subclasses of :class:`.TypeEngine` include
@@ -55,6 +73,8 @@ class TypeEngine(Traversible):
_is_array = False
_is_type_decorator = False
+ _block_from_type_affinity = False
+
render_bind_cast = False
"""Render bind casts for :attr:`.BindTyping.RENDER_CASTS` mode.
@@ -70,7 +90,10 @@ class TypeEngine(Traversible):
"""
- class Comparator(operators.ColumnOperators):
+ class Comparator(
+ ColumnOperators["ColumnElement"],
+ Generic[_CT],
+ ):
"""Base class for custom comparison operations defined at the
type level. See :attr:`.TypeEngine.comparator_factory`.
@@ -84,23 +107,33 @@ class TypeEngine(Traversible):
def __clause_element__(self):
return self.expr
- def __init__(self, expr):
+ def __init__(self, expr: "ColumnElement[_CT]"):
self.expr = expr
- self.type = expr.type
+ self.type: TypeEngine[_CT] = expr.type
@util.preload_module("sqlalchemy.sql.default_comparator")
- def operate(self, op, *other, **kwargs):
+ def operate(
+ self, op: "OperatorType", *other, **kwargs
+ ) -> "ColumnElement":
default_comparator = util.preloaded.sql_default_comparator
- o = default_comparator.operator_lookup[op.__name__]
- return o[0](self.expr, op, *(other + o[1:]), **kwargs)
+ op_fn, addtl_kw = default_comparator.operator_lookup[op.__name__]
+ if kwargs:
+ addtl_kw = addtl_kw.union(kwargs)
+ return op_fn(self.expr, op, *other, **addtl_kw)
@util.preload_module("sqlalchemy.sql.default_comparator")
- def reverse_operate(self, op, other, **kwargs):
+ def reverse_operate(
+ self, op: "OperatorType", other, **kwargs
+ ) -> "ColumnElement":
default_comparator = util.preloaded.sql_default_comparator
- o = default_comparator.operator_lookup[op.__name__]
- return o[0](self.expr, op, other, reverse=True, *o[1:], **kwargs)
-
- def _adapt_expression(self, op, other_comparator):
+ op_fn, addtl_kw = default_comparator.operator_lookup[op.__name__]
+ if kwargs:
+ addtl_kw = addtl_kw.union(kwargs)
+ return op_fn(self.expr, op, other, reverse=True, **addtl_kw)
+
+ def _adapt_expression(
+ self, op: "OperatorType", other_comparator
+ ) -> Tuple["OperatorType", "TypeEngine[_CT]"]:
"""evaluate the return type of <self> <op> <othertype>,
and apply any adaptations to the given operator.
@@ -611,7 +644,9 @@ class TypeEngine(Traversible):
for t in self.__class__.__mro__:
if t in (TypeEngine, UserDefinedType):
return typ
- elif issubclass(t, (TypeEngine, UserDefinedType)):
+ elif issubclass(
+ t, (TypeEngine, UserDefinedType)
+ ) and not t.__dict__.get("_block_from_type_affinity", False):
typ = t
else:
return self.__class__
@@ -1202,7 +1237,7 @@ class NativeForEmulated:
return cls(**kw)
-class TypeDecorator(ExternalType, SchemaEventTarget, TypeEngine):
+class TypeDecorator(ExternalType, SchemaEventTarget, TypeEngine[_T]):
"""Allows the creation of types which add additional functionality
to an existing type.
@@ -1882,7 +1917,9 @@ def _reconstitute_comparator(expression):
return expression.comparator
-def to_instance(typeobj, *arg, **kw):
+def to_instance(
+ typeobj: Union[Type[TypeEngine[_T]], TypeEngine[_T], None], *arg, **kw
+) -> TypeEngine[_T]:
if typeobj is None:
return NULLTYPE
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index 63067585e..fa3bae835 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -46,9 +46,35 @@ from .. import exc
from .. import util
-join_condition = util.langhelpers.public_factory(
- Join._join_condition, ".sql.util.join_condition"
-)
+def join_condition(a, b, a_subset=None, consider_as_foreign_keys=None):
+ """Create a join condition between two tables or selectables.
+
+ e.g.::
+
+ join_condition(tablea, tableb)
+
+ would produce an expression along the lines of::
+
+ tablea.c.id==tableb.c.tablea_id
+
+ The join is determined based on the foreign key relationships
+ between the two selectables. If there are multiple ways
+ to join, or no way to join, an error is raised.
+
+ :param a_subset: An optional expression that is a sub-component
+ of ``a``. An attempt will be made to join to just this sub-component
+ first before looking at the full ``a`` construct, and if found
+ will be successful even if there are other ways to join to ``a``.
+ This allows the "right side" of a join to be passed thereby
+ providing a "natural join".
+
+ """
+ return Join._join_condition(
+ a,
+ b,
+ a_subset=a_subset,
+ consider_as_foreign_keys=consider_as_foreign_keys,
+ )
def find_join_source(clauses, join_to):