# register default adapters for PostgreSQL
adapters = postgres.adapters # exposed by the package
-postgres.register_default_adapters(adapters)
# DBAPI compliancy
connect = Connection.connect
adapters.register_dumper(Binary, BinaryTextDumper) # dbapi20
adapters.register_dumper(Binary, BinaryBinaryDumper) # dbapi20
+# After registering the dbapi20 dumpers to clobber the oid they set
+postgres.register_default_adapters(adapters)
# Note: defining the exported methods helps both Sphynx in documenting that
# this is the canonical place to obtain them and should be used by MyPy too,
types: TypesRegistry
_dumpers: Dict[PyFormat, Dict[Union[type, str], Type[Dumper]]]
+ _dumpers_by_oid: List[Dict[int, Type[Dumper]]]
_loaders: List[Dict[int, Type[Loader]]]
# Record if a dumper or loader has an optimised version.
self._dumpers = template._dumpers.copy()
self._own_dumpers = _dumpers_shared.copy()
template._own_dumpers = _dumpers_shared.copy()
+
+ self._dumpers_by_oid = template._dumpers_by_oid[:]
+ self._own_dumpers_by_oid = [False, False]
+ template._own_dumpers_by_oid = [False, False]
+
self._loaders = template._loaders[:]
self._own_loaders = [False, False]
template._own_loaders = [False, False]
+
self.types = TypesRegistry(template.types)
+
else:
self._dumpers = {fmt: {} for fmt in PyFormat}
self._own_dumpers = _dumpers_owned.copy()
+
+ self._dumpers_by_oid = [{}, {}]
+ self._own_dumpers_by_oid = [True, True]
+
self._loaders = [{}, {}]
self._own_loaders = [True, True]
+
self.types = types or TypesRegistry()
# implement the AdaptContext protocol too
self._dumpers[fmt][cls] = dumper
+ # Register the dumper by oid, if the oid of the dumper is fixed
+ if dumper.oid:
+ if not self._own_dumpers_by_oid[dumper.format]:
+ self._dumpers_by_oid[dumper.format] = self._dumpers_by_oid[
+ dumper.format
+ ].copy()
+ self._own_dumpers_by_oid[dumper.format] = True
+
+ self._dumpers_by_oid[dumper.format][dumper.oid] = dumper
+
def register_loader(
self, oid: Union[int, str], loader: Type["Loader"]
) -> None:
"""
format: pq.Format
+ """The format this dumper produces (class attirbute)."""
+
oid: int
- """The oid to pass to the server, if known; 0 otherwise."""
+ """The oid to pass to the server, if known; 0 otherwise (class attribute)."""
def __init__(self, cls: type, context: Optional[AdaptContext] = None):
...
adapters = context.adapters
adapters.register_dumper("datetime.date", DateDumper)
adapters.register_dumper("datetime.date", DateBinaryDumper)
+
+ # first register dumpers for 'timetz' oid, then the proper ones on time type.
+ adapters.register_dumper("datetime.time", TimeTzDumper)
+ adapters.register_dumper("datetime.time", TimeTzBinaryDumper)
adapters.register_dumper("datetime.time", TimeDumper)
adapters.register_dumper("datetime.time", TimeBinaryDumper)
+
+ # first register dumpers for 'timestamp' oid, then the proper ones
+ # on the datetime type.
+ adapters.register_dumper("datetime.datetime", DatetimeNoTzDumper)
+ adapters.register_dumper("datetime.datetime", DatetimeNoTzBinaryDumper)
adapters.register_dumper("datetime.datetime", DatetimeDumper)
adapters.register_dumper("datetime.datetime", DatetimeBinaryDumper)
+
adapters.register_dumper("datetime.timedelta", TimedeltaDumper)
adapters.register_dumper("datetime.timedelta", TimedeltaBinaryDumper)
adapters.register_loader("date", DateLoader)
adapters.register_dumper(int, IntBinaryDumper)
adapters.register_dumper(float, FloatDumper)
adapters.register_dumper(float, FloatBinaryDumper)
- # The binary dumper is currently some 30% slower, so default to text
- # (see tests/scripts/testdec.py for a rough benchmark)
- adapters.register_dumper("decimal.Decimal", DecimalBinaryDumper)
- adapters.register_dumper("decimal.Decimal", DecimalDumper)
adapters.register_dumper(Int2, Int2Dumper)
adapters.register_dumper(Int4, Int4Dumper)
adapters.register_dumper(Int8, Int8Dumper)
adapters.register_dumper(IntNumeric, IntNumericDumper)
adapters.register_dumper(Oid, OidDumper)
+
+ # The binary dumper is currently some 30% slower, so default to text
+ # (see tests/scripts/testdec.py for a rough benchmark)
+ # Also, must be after IntNumericDumper
+ adapters.register_dumper("decimal.Decimal", DecimalBinaryDumper)
+ adapters.register_dumper("decimal.Decimal", DecimalDumper)
+
adapters.register_dumper(Float4, Float4Dumper)
adapters.register_dumper(Float8, FloatDumper)
adapters.register_dumper(Int2, Int2BinaryDumper)
def register_default_adapters(context: AdaptContext) -> None:
adapters = context.adapters
- # NOTE: the order the dumpers are registered is relevant.
- # The last one registered becomes the default for each type.
- # Normally, binary is the default dumper, except for text (which plays
- # the role of unknown, so it can be cast automatically to other types).
+ # NOTE: the order the dumpers are registered is relevant. The last one
+ # registered becomes the default for each type. Usually, binary is the
+ # default dumper. For text we use the text dumper as default because it
+ # plays the role of unknown, and it can be cast automatically to other
+ # types. However, before that, we register a dumper with the text oid,
+ # which will be used when a text dumper is looked up by oid.
adapters.register_dumper(str, StrBinaryDumper)
+ adapters.register_dumper(str, StrDumper)
adapters.register_dumper(str, StrDumperUnknown)
adapters.register_loader(postgres.INVALID_OID, TextLoader)
from psycopg import adapters
dumpers = deepcopy(adapters._dumpers)
+ dumpers_by_oid = deepcopy(adapters._dumpers_by_oid)
loaders = deepcopy(adapters._loaders)
types = list(adapters.types)
yield None
adapters._dumpers = dumpers
+ adapters._dumpers_by_oid = dumpers_by_oid
adapters._loaders = loaders
adapters.types.clear()
for t in types: