summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/elements.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-01-02 17:35:43 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2022-01-12 10:29:30 -0500
commit43f6ae639ca0186f4802255861acdc20f19e702f (patch)
tree311d908ba5b72b0fcb751d682f56ccd73710d41b /lib/sqlalchemy/sql/elements.py
parenta869dc8fe3cd579ed9bab665d215a6c3e3d8a4ca (diff)
downloadsqlalchemy-43f6ae639ca0186f4802255861acdc20f19e702f.tar.gz
initial reorganize for static typing
start applying foundational annotations to key elements. two main elements addressed here: 1. removal of public_factory() and replacement with explicit functions. this just works much better with typing. 2. typing support for column expressions and operators. The biggest part of this involves stubbing out all the ColumnOperators methods under ColumnElement in a TYPE_CHECKING section. Took me a while to see this method vs. much more complicated things I thought I needed. Also for this version implementing #7519, ColumnElement types against the Python type and not TypeEngine. it is hoped this leads to easier transferrence between ORM/Core as well as eventual support for result set typing. Not clear yet how well this approach will work and what new issues it may introduce. given the current approach we now get full, rich typing for scenarios like this: from sqlalchemy import column, Integer, String, Boolean c1 = column('a', String) c2 = column('a', Integer) expr1 = c2.in_([1, 2, 3]) expr2 = c2 / 5 expr3 = -c2 expr4_a = ~(c2 == 5) expr4_b = ~column('q', Boolean) expr5 = c1 + 'x' expr6 = c2 + 10 Fixes: #7519 Fixes: #6810 Change-Id: I078d9f57955549f6f7868314287175f6c61c44cb
Diffstat (limited to 'lib/sqlalchemy/sql/elements.py')
-rw-r--r--lib/sqlalchemy/sql/elements.py1978
1 files changed, 488 insertions, 1490 deletions
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("_")