summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/dialects/postgresql/ranges.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-09-20 12:21:14 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2022-09-20 12:34:46 -0400
commit9ae645d5d1a8cc7732a6d335be6205d0b21e31b1 (patch)
tree00265efd4658b42bf7bd3d794dc82568a73f7ea1 /lib/sqlalchemy/dialects/postgresql/ranges.py
parent214d1ad7c38deebec6547da22b99c0a4804bf820 (diff)
downloadsqlalchemy-9ae645d5d1a8cc7732a6d335be6205d0b21e31b1.tar.gz
auto-cast PG range types
Range type handling has been enhanced so that it automatically renders type casts, so that in-place round trips for statements that don't provide the database with any context don't require the :func:`_sql.cast` construct to be explicit for the database to know the desired type. Change-Id: Id630b726f8a23059dd2f4cbc410bf5229d89cbfb References: #8540
Diffstat (limited to 'lib/sqlalchemy/dialects/postgresql/ranges.py')
-rw-r--r--lib/sqlalchemy/dialects/postgresql/ranges.py39
1 files changed, 39 insertions, 0 deletions
diff --git a/lib/sqlalchemy/dialects/postgresql/ranges.py b/lib/sqlalchemy/dialects/postgresql/ranges.py
index edbe165d9..327feb409 100644
--- a/lib/sqlalchemy/dialects/postgresql/ranges.py
+++ b/lib/sqlalchemy/dialects/postgresql/ranges.py
@@ -91,6 +91,35 @@ class AbstractRange(sqltypes.TypeEngine):
""" # noqa: E501
+ render_bind_cast = True
+
+ def adapt(self, impltype):
+ """dynamically adapt a range type to an abstract impl.
+
+ For example ``INT4RANGE().adapt(_Psycopg2NumericRange)`` should
+ produce a type that will have ``_Psycopg2NumericRange`` behaviors
+ and also render as ``INT4RANGE`` in SQL and DDL.
+
+ """
+ if issubclass(impltype, AbstractRangeImpl):
+ # 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.
+ # I'm choosing #1 as the resulting type object
+ # will then make use of the same mechanics
+ # as if we had made all these sub-types explicitly, and will
+ # also look more obvious under pdb etc.
+ # The adapt() operation here is cached per type-class-per-dialect,
+ # so is not much of a performance concern
+ visit_name = self.__visit_name__
+ return type(
+ f"{visit_name}RangeImpl",
+ (impltype, self.__class__),
+ {"__visit_name__": visit_name},
+ )()
+ else:
+ return super().adapt(impltype)
+
class comparator_factory(sqltypes.Concatenable.Comparator):
"""Define comparison operations for range types."""
@@ -165,10 +194,20 @@ class AbstractRange(sqltypes.TypeEngine):
return self.expr.op("+")(other)
+class AbstractRangeImpl(AbstractRange):
+ """marker for AbstractRange that will apply a subclass-specific
+ adaptation"""
+
+
class AbstractMultiRange(AbstractRange):
"""base for PostgreSQL MULTIRANGE types"""
+class AbstractMultiRangeImpl(AbstractRangeImpl, AbstractMultiRange):
+ """marker for AbstractRange that will apply a subclass-specific
+ adaptation"""
+
+
class INT4RANGE(AbstractRange):
"""Represent the PostgreSQL INT4RANGE type."""