main commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-16 16:30:25 +09:00
parent 91c7e04474
commit 537e7b363f
1146 changed files with 45926 additions and 77196 deletions

View File

@@ -1,5 +1,4 @@
# dialects/postgresql/ranges.py
# Copyright (C) 2013-2025 the SQLAlchemy authors and contributors
# Copyright (C) 2013-2023 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@@ -15,10 +14,8 @@ from decimal import Decimal
from typing import Any
from typing import cast
from typing import Generic
from typing import List
from typing import Optional
from typing import overload
from typing import Sequence
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
@@ -154,8 +151,8 @@ class Range(Generic[_T]):
return not self.empty and self.upper is None
@property
def __sa_type_engine__(self) -> AbstractSingleRange[_T]:
return AbstractSingleRange()
def __sa_type_engine__(self) -> AbstractRange[Range[_T]]:
return AbstractRange()
def _contains_value(self, value: _T) -> bool:
"""Return True if this range contains the given value."""
@@ -271,9 +268,9 @@ class Range(Generic[_T]):
value2 += step
value2_inc = False
if value1 < value2:
if value1 < value2: # type: ignore
return -1
elif value1 > value2:
elif value1 > value2: # type: ignore
return 1
elif only_values:
return 0
@@ -360,8 +357,6 @@ class Range(Generic[_T]):
else:
return self._contains_value(value)
__contains__ = contains
def overlaps(self, other: Range[_T]) -> bool:
"Determine whether this range overlaps with `other`."
@@ -712,46 +707,27 @@ class Range(Generic[_T]):
return f"{b0}{l},{r}{b1}"
class MultiRange(List[Range[_T]]):
"""Represents a multirange sequence.
This list subclass is an utility to allow automatic type inference of
the proper multi-range SQL type depending on the single range values.
This is useful when operating on literal multi-ranges::
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import MultiRange, Range
value = literal(MultiRange([Range(2, 4)]))
select(tbl).where(tbl.c.value.op("@")(MultiRange([Range(-3, 7)])))
.. versionadded:: 2.0.26
class AbstractRange(sqltypes.TypeEngine[Range[_T]]):
"""
Base for PostgreSQL RANGE types.
.. seealso::
- :ref:`postgresql_multirange_list_use`.
"""
`PostgreSQL range functions <https://www.postgresql.org/docs/current/static/functions-range.html>`_
@property
def __sa_type_engine__(self) -> AbstractMultiRange[_T]:
return AbstractMultiRange()
class AbstractRange(sqltypes.TypeEngine[_T]):
"""Base class for single and multi Range SQL types."""
""" # noqa: E501
render_bind_cast = True
__abstract__ = True
@overload
def adapt(self, cls: Type[_TE], **kw: Any) -> _TE: ...
def adapt(self, cls: Type[_TE], **kw: Any) -> _TE:
...
@overload
def adapt(
self, cls: Type[TypeEngineMixin], **kw: Any
) -> TypeEngine[Any]: ...
def adapt(self, cls: Type[TypeEngineMixin], **kw: Any) -> TypeEngine[Any]:
...
def adapt(
self,
@@ -765,10 +741,7 @@ class AbstractRange(sqltypes.TypeEngine[_T]):
and also render as ``INT4RANGE`` in SQL and DDL.
"""
if (
issubclass(cls, (AbstractSingleRangeImpl, AbstractMultiRangeImpl))
and cls is not self.__class__
):
if issubclass(cls, AbstractRangeImpl) and cls is not self.__class__:
# two ways to do this are: 1. create a new type on the fly
# or 2. have AbstractRangeImpl(visit_name) constructor and a
# visit_abstract_range_impl() method in the PG compiler.
@@ -787,6 +760,21 @@ class AbstractRange(sqltypes.TypeEngine[_T]):
else:
return super().adapt(cls)
def _resolve_for_literal(self, value: Any) -> Any:
spec = value.lower if value.lower is not None else value.upper
if isinstance(spec, int):
return INT8RANGE()
elif isinstance(spec, (Decimal, float)):
return NUMRANGE()
elif isinstance(spec, datetime):
return TSRANGE() if not spec.tzinfo else TSTZRANGE()
elif isinstance(spec, date):
return DATERANGE()
else:
# empty Range, SQL datatype can't be determined here
return sqltypes.NULLTYPE
class comparator_factory(TypeEngine.Comparator[Range[Any]]):
"""Define comparison operations for range types."""
@@ -868,164 +856,91 @@ class AbstractRange(sqltypes.TypeEngine[_T]):
return self.expr.operate(operators.mul, other)
class AbstractSingleRange(AbstractRange[Range[_T]]):
"""Base for PostgreSQL RANGE types.
These are types that return a single :class:`_postgresql.Range` object.
.. seealso::
`PostgreSQL range functions <https://www.postgresql.org/docs/current/static/functions-range.html>`_
""" # noqa: E501
__abstract__ = True
def _resolve_for_literal(self, value: Range[Any]) -> Any:
spec = value.lower if value.lower is not None else value.upper
if isinstance(spec, int):
# pg is unreasonably picky here: the query
# "select 1::INTEGER <@ '[1, 4)'::INT8RANGE" raises
# "operator does not exist: integer <@ int8range" as of pg 16
if _is_int32(value):
return INT4RANGE()
else:
return INT8RANGE()
elif isinstance(spec, (Decimal, float)):
return NUMRANGE()
elif isinstance(spec, datetime):
return TSRANGE() if not spec.tzinfo else TSTZRANGE()
elif isinstance(spec, date):
return DATERANGE()
else:
# empty Range, SQL datatype can't be determined here
return sqltypes.NULLTYPE
class AbstractSingleRangeImpl(AbstractSingleRange[_T]):
"""Marker for AbstractSingleRange that will apply a subclass-specific
class AbstractRangeImpl(AbstractRange[Range[_T]]):
"""Marker for AbstractRange that will apply a subclass-specific
adaptation"""
class AbstractMultiRange(AbstractRange[Sequence[Range[_T]]]):
"""Base for PostgreSQL MULTIRANGE types.
these are types that return a sequence of :class:`_postgresql.Range`
objects.
"""
class AbstractMultiRange(AbstractRange[Range[_T]]):
"""base for PostgreSQL MULTIRANGE types"""
__abstract__ = True
def _resolve_for_literal(self, value: Sequence[Range[Any]]) -> Any:
if not value:
# empty MultiRange, SQL datatype can't be determined here
return sqltypes.NULLTYPE
first = value[0]
spec = first.lower if first.lower is not None else first.upper
if isinstance(spec, int):
# pg is unreasonably picky here: the query
# "select 1::INTEGER <@ '{[1, 4),[6,19)}'::INT8MULTIRANGE" raises
# "operator does not exist: integer <@ int8multirange" as of pg 16
if all(_is_int32(r) for r in value):
return INT4MULTIRANGE()
else:
return INT8MULTIRANGE()
elif isinstance(spec, (Decimal, float)):
return NUMMULTIRANGE()
elif isinstance(spec, datetime):
return TSMULTIRANGE() if not spec.tzinfo else TSTZMULTIRANGE()
elif isinstance(spec, date):
return DATEMULTIRANGE()
else:
# empty Range, SQL datatype can't be determined here
return sqltypes.NULLTYPE
class AbstractMultiRangeImpl(AbstractMultiRange[_T]):
"""Marker for AbstractMultiRange that will apply a subclass-specific
class AbstractMultiRangeImpl(
AbstractRangeImpl[Range[_T]], AbstractMultiRange[Range[_T]]
):
"""Marker for AbstractRange that will apply a subclass-specific
adaptation"""
class INT4RANGE(AbstractSingleRange[int]):
class INT4RANGE(AbstractRange[Range[int]]):
"""Represent the PostgreSQL INT4RANGE type."""
__visit_name__ = "INT4RANGE"
class INT8RANGE(AbstractSingleRange[int]):
class INT8RANGE(AbstractRange[Range[int]]):
"""Represent the PostgreSQL INT8RANGE type."""
__visit_name__ = "INT8RANGE"
class NUMRANGE(AbstractSingleRange[Decimal]):
class NUMRANGE(AbstractRange[Range[Decimal]]):
"""Represent the PostgreSQL NUMRANGE type."""
__visit_name__ = "NUMRANGE"
class DATERANGE(AbstractSingleRange[date]):
class DATERANGE(AbstractRange[Range[date]]):
"""Represent the PostgreSQL DATERANGE type."""
__visit_name__ = "DATERANGE"
class TSRANGE(AbstractSingleRange[datetime]):
class TSRANGE(AbstractRange[Range[datetime]]):
"""Represent the PostgreSQL TSRANGE type."""
__visit_name__ = "TSRANGE"
class TSTZRANGE(AbstractSingleRange[datetime]):
class TSTZRANGE(AbstractRange[Range[datetime]]):
"""Represent the PostgreSQL TSTZRANGE type."""
__visit_name__ = "TSTZRANGE"
class INT4MULTIRANGE(AbstractMultiRange[int]):
class INT4MULTIRANGE(AbstractMultiRange[Range[int]]):
"""Represent the PostgreSQL INT4MULTIRANGE type."""
__visit_name__ = "INT4MULTIRANGE"
class INT8MULTIRANGE(AbstractMultiRange[int]):
class INT8MULTIRANGE(AbstractMultiRange[Range[int]]):
"""Represent the PostgreSQL INT8MULTIRANGE type."""
__visit_name__ = "INT8MULTIRANGE"
class NUMMULTIRANGE(AbstractMultiRange[Decimal]):
class NUMMULTIRANGE(AbstractMultiRange[Range[Decimal]]):
"""Represent the PostgreSQL NUMMULTIRANGE type."""
__visit_name__ = "NUMMULTIRANGE"
class DATEMULTIRANGE(AbstractMultiRange[date]):
class DATEMULTIRANGE(AbstractMultiRange[Range[date]]):
"""Represent the PostgreSQL DATEMULTIRANGE type."""
__visit_name__ = "DATEMULTIRANGE"
class TSMULTIRANGE(AbstractMultiRange[datetime]):
class TSMULTIRANGE(AbstractMultiRange[Range[datetime]]):
"""Represent the PostgreSQL TSRANGE type."""
__visit_name__ = "TSMULTIRANGE"
class TSTZMULTIRANGE(AbstractMultiRange[datetime]):
class TSTZMULTIRANGE(AbstractMultiRange[Range[datetime]]):
"""Represent the PostgreSQL TSTZRANGE type."""
__visit_name__ = "TSTZMULTIRANGE"
_max_int_32 = 2**31 - 1
_min_int_32 = -(2**31)
def _is_int32(r: Range[int]) -> bool:
return (r.lower is None or _min_int_32 <= r.lower <= _max_int_32) and (
r.upper is None or _min_int_32 <= r.upper <= _max_int_32
)