mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 18:33:44 +03:00
Added documentation for range types and adaptation
This commit is contained in:
parent
b1953640d2
commit
c756d580f2
1
NEWS
1
NEWS
|
@ -1,6 +1,7 @@
|
||||||
What's new in psycopg 2.4.6
|
What's new in psycopg 2.4.6
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
- Added support for PostgreSQL 9.2 range types.
|
||||||
- Added support for backward scrollable cursors. Thanks to Jon Nelson
|
- Added support for backward scrollable cursors. Thanks to Jon Nelson
|
||||||
for the initial patch (ticket #108).
|
for the initial patch (ticket #108).
|
||||||
- connection.reset() implemented using DISCARD ALL on server versions
|
- connection.reset() implemented using DISCARD ALL on server versions
|
||||||
|
|
|
@ -211,6 +211,89 @@ requires no adapter registration.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
pair: range; Data types
|
||||||
|
|
||||||
|
Range data types
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. versionadded:: 2.4.6
|
||||||
|
|
||||||
|
Psycopg offers a `Range` Python type and supports adaptation between them and
|
||||||
|
PostgreSQL |range|_ types. Builtin |range| types are supported out-of-the-box;
|
||||||
|
user-defined |range| types can be adapted using `register_range()`.
|
||||||
|
|
||||||
|
.. |range| replace:: :sql:`range`
|
||||||
|
.. _range: http://www.postgresql.org/docs/current/static/rangetypes.html
|
||||||
|
|
||||||
|
.. autoclass:: Range
|
||||||
|
|
||||||
|
This Python type is only used to pass and retrieve range values to and
|
||||||
|
from PostgreSQL and doesn't attempt to replicate the PostgreSQL range
|
||||||
|
features: it doesn't perform normalization and doesn't implement all the
|
||||||
|
operators__ supported by the database.
|
||||||
|
|
||||||
|
.. __: http://www.postgresql.org/docs/current/static/functions-range.html#RANGE-OPERATORS-TABLE
|
||||||
|
|
||||||
|
`!Range` objects are immutable, hashable, and support the ``in`` operator
|
||||||
|
(checking if an element is within the range). They can be tested for
|
||||||
|
equivalence but not for ordering. Empty ranges evaluate to `!False` in
|
||||||
|
boolean context, nonempty evaluate to `!True`.
|
||||||
|
|
||||||
|
Although it is possible to instantiate `!Range` objects, the class doesn't
|
||||||
|
have an adapter registered, so you cannot normally pass these instances as
|
||||||
|
query arguments. To use range objects as query arguments you can either
|
||||||
|
use one of the provided subclasses, such as `NumericRange` or create a
|
||||||
|
custom subclass using `register_range()`.
|
||||||
|
|
||||||
|
Object attributes:
|
||||||
|
|
||||||
|
.. autoattribute:: isempty
|
||||||
|
.. autoattribute:: lower
|
||||||
|
.. autoattribute:: upper
|
||||||
|
.. autoattribute:: lower_inc
|
||||||
|
.. autoattribute:: upper_inc
|
||||||
|
.. autoattribute:: lower_inf
|
||||||
|
.. autoattribute:: upper_inf
|
||||||
|
|
||||||
|
|
||||||
|
The following `Range` subclasses map builtin PostgreSQL |range| types to
|
||||||
|
Python objects: they have an adapter registered so their instances can be
|
||||||
|
passed as query arguments. |range| values read from database queries are
|
||||||
|
automatically casted into instances of these classes.
|
||||||
|
|
||||||
|
.. autoclass:: NumericRange
|
||||||
|
.. autoclass:: DateRange
|
||||||
|
.. autoclass:: DateTimeRange
|
||||||
|
.. autoclass:: DateTimeTZRange
|
||||||
|
|
||||||
|
Custom |range| types (created with |CREATE TYPE|_ :sql:`... AS RANGE`) can be
|
||||||
|
adapted to a custom `Range` subclass:
|
||||||
|
|
||||||
|
.. autofunction:: register_range
|
||||||
|
|
||||||
|
.. autoclass:: RangeCaster
|
||||||
|
|
||||||
|
Object attributes:
|
||||||
|
|
||||||
|
.. attribute:: range
|
||||||
|
|
||||||
|
The `!Range` subclass adapted.
|
||||||
|
|
||||||
|
.. attribute:: adapter
|
||||||
|
|
||||||
|
The `~psycopg2.extensions.ISQLQuote` responsible to adapt `!range`.
|
||||||
|
|
||||||
|
.. attribute:: typecaster
|
||||||
|
|
||||||
|
The object responsible for casting.
|
||||||
|
|
||||||
|
.. attribute:: array_typecaster
|
||||||
|
|
||||||
|
The object responsible for casting arrays, if available, else `!None`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
pair: UUID; Data types
|
pair: UUID; Data types
|
||||||
|
|
||||||
|
|
|
@ -31,26 +31,13 @@ from psycopg2.extensions import ISQLQuote, adapt, register_adapter
|
||||||
from psycopg2.extensions import new_type, new_array_type, register_type
|
from psycopg2.extensions import new_type, new_array_type, register_type
|
||||||
|
|
||||||
class Range(object):
|
class Range(object):
|
||||||
"""Python representation for a PostgreSQL range type.
|
"""Python representation for a PostgreSQL |range|_ type.
|
||||||
|
|
||||||
:param lower: lower bound for the range. None means unbound
|
:param lower: lower bound for the range. `!None` means unbound
|
||||||
:param upper: upper bound for the range. None means unbound
|
:param upper: upper bound for the range. `!None` means unbound
|
||||||
:param bounds: one of the literal strings ``()``, ``[)``, ``(]``, ``[]``,
|
:param bounds: one of the literal strings ``()``, ``[)``, ``(]``, ``[]``,
|
||||||
representing whether the lower or upper bounds are included
|
representing whether the lower or upper bounds are included
|
||||||
:param empty: if true, the range is empty
|
:param empty: if `!True`, the range is empty
|
||||||
|
|
||||||
TODO: move this to the docs
|
|
||||||
|
|
||||||
This Python type is only used to pass and retrieve range values to and
|
|
||||||
from PostgreSQL and doesn't attempt to replicate the PostgreSQL range
|
|
||||||
features: it doesn't perform normalization and doesn't implement all the
|
|
||||||
operators supported.
|
|
||||||
|
|
||||||
Although it is possible to instantiate `!Range` objects, the class doesn't
|
|
||||||
have an adapter so you cannot normally pass these instances as query
|
|
||||||
arguments. To use range objects as query arguments you can either use one
|
|
||||||
of the provided subclasses, such as [TODO: the other] `IntRange` or create
|
|
||||||
a custom one using `register_range()`.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
__slots__ = ('_lower', '_upper', '_bounds')
|
__slots__ = ('_lower', '_upper', '_bounds')
|
||||||
|
@ -75,41 +62,41 @@ class Range(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lower(self):
|
def lower(self):
|
||||||
"""The lower bound of the range. None if empty or unbound."""
|
"""The lower bound of the range. `!None` if empty or unbound."""
|
||||||
return self._lower
|
return self._lower
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def upper(self):
|
def upper(self):
|
||||||
"""The upper bound of the range. None if empty or unbound."""
|
"""The upper bound of the range. `!None` if empty or unbound."""
|
||||||
return self._upper
|
return self._upper
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def isempty(self):
|
def isempty(self):
|
||||||
"""True if the range is empty."""
|
"""`!True` if the range is empty."""
|
||||||
return self._bounds is None
|
return self._bounds is None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lower_inf(self):
|
def lower_inf(self):
|
||||||
"""True if the range doesn't have a lower bound."""
|
"""`!True` if the range doesn't have a lower bound."""
|
||||||
if self._bounds is None: return False
|
if self._bounds is None: return False
|
||||||
return self._lower is None
|
return self._lower is None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def upper_inf(self):
|
def upper_inf(self):
|
||||||
"""True if the range doesn't have an upper bound."""
|
"""`!True` if the range doesn't have an upper bound."""
|
||||||
if self._bounds is None: return False
|
if self._bounds is None: return False
|
||||||
return self._upper is None
|
return self._upper is None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lower_inc(self):
|
def lower_inc(self):
|
||||||
"""True if the lower bound is included in the range."""
|
"""`!True` if the lower bound is included in the range."""
|
||||||
if self._bounds is None: return False
|
if self._bounds is None: return False
|
||||||
if self._lower is None: return False
|
if self._lower is None: return False
|
||||||
return self._bounds[0] == '['
|
return self._bounds[0] == '['
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def upper_inc(self):
|
def upper_inc(self):
|
||||||
"""True if the upper bound is included in the range."""
|
"""`!True` if the upper bound is included in the range."""
|
||||||
if self._bounds is None: return False
|
if self._bounds is None: return False
|
||||||
if self._upper is None: return False
|
if self._upper is None: return False
|
||||||
return self._bounds[1] == ']'
|
return self._bounds[1] == ']'
|
||||||
|
@ -151,19 +138,30 @@ class Range(object):
|
||||||
|
|
||||||
|
|
||||||
def register_range(pgrange, pyrange, conn_or_curs, globally=False):
|
def register_range(pgrange, pyrange, conn_or_curs, globally=False):
|
||||||
"""Register a typecaster and an adapter for range a range type.
|
"""Create and register an adapter and the typecasters to convert between
|
||||||
|
a PostgreSQL |range|_ type and a PostgreSQL `Range` subclass.
|
||||||
:param pgrange: the name of the PostgreSQL range type
|
|
||||||
:param pyrange: a Range (strict) subclass, or just the name to give it
|
:param pgrange: the name of the PostgreSQL |range| type. Can be
|
||||||
(the class will be available as the `!range` attribute of the returned
|
schema-qualified
|
||||||
`RangeCaster`.
|
:param pyrange: a `Range` strict subclass, or just a name to give to a new
|
||||||
|
class
|
||||||
:param conn_or_curs: a connection or cursor used to find the oid of the
|
:param conn_or_curs: a connection or cursor used to find the oid of the
|
||||||
range and its subtype; the typecaster is registered in a scope limited
|
range and its subtype; the typecaster is registered in a scope limited
|
||||||
to this object, unless *globally* is set to `!True`
|
to this object, unless *globally* is set to `!True`
|
||||||
:param globally: if `!False` (default) register the typecaster only on
|
:param globally: if `!False` (default) register the typecaster only on
|
||||||
*conn_or_curs*, otherwise register it globally
|
*conn_or_curs*, otherwise register it globally
|
||||||
:return: the registered `RangeCaster` instance responsible for the
|
:return: `RangeCaster` instance responsible for the conversion
|
||||||
conversion
|
|
||||||
|
If a string is passed to *pyrange*, a new `Range` subclass is created
|
||||||
|
with such name and will be available as the `~RangeCaster.range` attribute
|
||||||
|
of the returned `RangeCaster` object.
|
||||||
|
|
||||||
|
The function queries the database on *conn_or_curs* to inspect the
|
||||||
|
*pgrange* type. Raise `~psycopg2.ProgrammingError` if the type is not
|
||||||
|
found. If querying the database is not advisable, use directly the
|
||||||
|
`RangeCaster` class and register the adapter and typecasters using the
|
||||||
|
provided functions.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
caster = RangeCaster._from_db(pgrange, pyrange, conn_or_curs)
|
caster = RangeCaster._from_db(pgrange, pyrange, conn_or_curs)
|
||||||
caster._register(not globally and conn_or_curs or None)
|
caster._register(not globally and conn_or_curs or None)
|
||||||
|
@ -219,7 +217,12 @@ class RangeAdapter(object):
|
||||||
|
|
||||||
|
|
||||||
class RangeCaster(object):
|
class RangeCaster(object):
|
||||||
"""Helper class to convert between `Range` and PostgreSQL range types."""
|
"""Helper class to convert between `Range` and PostgreSQL range types.
|
||||||
|
|
||||||
|
Objects of this class are usually created by `register_range()`. Manual
|
||||||
|
creation could be useful if querying the database is not advisable: in
|
||||||
|
this case the oids must be provided.
|
||||||
|
"""
|
||||||
def __init__(self, pgrange, pyrange, oid, subtype_oid, array_oid=None):
|
def __init__(self, pgrange, pyrange, oid, subtype_oid, array_oid=None):
|
||||||
self.subtype_oid = subtype_oid
|
self.subtype_oid = subtype_oid
|
||||||
self._create_ranges(pgrange, pyrange)
|
self._create_ranges(pgrange, pyrange)
|
||||||
|
@ -237,7 +240,9 @@ class RangeCaster(object):
|
||||||
def _create_ranges(self, pgrange, pyrange):
|
def _create_ranges(self, pgrange, pyrange):
|
||||||
"""Create Range and RangeAdapter classes if needed."""
|
"""Create Range and RangeAdapter classes if needed."""
|
||||||
# if got a string create a new RangeAdapter concrete type (with a name)
|
# if got a string create a new RangeAdapter concrete type (with a name)
|
||||||
# else take it as an adapter.
|
# else take it as an adapter. Passing an adapter should be considered
|
||||||
|
# an implementation detail and is not documented. It is currently used
|
||||||
|
# for the numeric ranges.
|
||||||
self.adapter = None
|
self.adapter = None
|
||||||
if isinstance(pgrange, basestring):
|
if isinstance(pgrange, basestring):
|
||||||
self.adapter = type(pgrange, (RangeAdapter,), {})
|
self.adapter = type(pgrange, (RangeAdapter,), {})
|
||||||
|
@ -378,7 +383,11 @@ where typname = %s and ns.nspname = %s;
|
||||||
|
|
||||||
|
|
||||||
class NumericRange(Range):
|
class NumericRange(Range):
|
||||||
"""A `Range` suitable to pass Python numeric types to a PostgreSQL range."""
|
"""A `Range` suitable to pass Python numeric types to a PostgreSQL range.
|
||||||
|
|
||||||
|
PostgreSQL types :sql:`int4range`, :sql:`int8range`, :sql:`numrange` are
|
||||||
|
casted into `!NumericRange` instances.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class DateRange(Range):
|
class DateRange(Range):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user