From: Daniele Varrazzo Date: Fri, 27 Aug 2021 00:17:53 +0000 (+0200) Subject: Add mapping oid -> Dumper in AdaptersMap X-Git-Tag: 3.0.beta1~29^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a91201dc8a547baf8d1b2de32fdb76e594a40cb6;p=thirdparty%2Fpsycopg.git Add mapping oid -> Dumper in AdaptersMap --- diff --git a/psycopg/psycopg/__init__.py b/psycopg/psycopg/__init__.py index 18894ccb3..acc4fbb70 100644 --- a/psycopg/psycopg/__init__.py +++ b/psycopg/psycopg/__init__.py @@ -37,7 +37,6 @@ if logger.level == logging.NOTSET: # register default adapters for PostgreSQL adapters = postgres.adapters # exposed by the package -postgres.register_default_adapters(adapters) # DBAPI compliancy connect = Connection.connect @@ -47,6 +46,8 @@ paramstyle = "pyformat" 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, diff --git a/psycopg/psycopg/_adapters_map.py b/psycopg/psycopg/_adapters_map.py index b572e7818..d0ddb3caf 100644 --- a/psycopg/psycopg/_adapters_map.py +++ b/psycopg/psycopg/_adapters_map.py @@ -57,6 +57,7 @@ class AdaptersMap: 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. @@ -71,15 +72,27 @@ class AdaptersMap: 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 @@ -127,6 +140,16 @@ class AdaptersMap: 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: diff --git a/psycopg/psycopg/abc.py b/psycopg/psycopg/abc.py index fffc5ba4d..cc7d72572 100644 --- a/psycopg/psycopg/abc.py +++ b/psycopg/psycopg/abc.py @@ -87,8 +87,10 @@ class Dumper(Protocol): """ 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): ... diff --git a/psycopg/psycopg/types/datetime.py b/psycopg/psycopg/types/datetime.py index f423d5088..f501fbd38 100644 --- a/psycopg/psycopg/types/datetime.py +++ b/psycopg/psycopg/types/datetime.py @@ -770,10 +770,20 @@ def register_default_adapters(context: 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) diff --git a/psycopg/psycopg/types/numeric.py b/psycopg/psycopg/types/numeric.py index 0b126132c..c11828e77 100644 --- a/psycopg/psycopg/types/numeric.py +++ b/psycopg/psycopg/types/numeric.py @@ -454,15 +454,18 @@ def register_default_adapters(context: AdaptContext) -> None: 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) diff --git a/psycopg/psycopg/types/string.py b/psycopg/psycopg/types/string.py index f25668956..a65fd3fbd 100644 --- a/psycopg/psycopg/types/string.py +++ b/psycopg/psycopg/types/string.py @@ -185,11 +185,14 @@ class ByteaBinaryLoader(Loader): 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) diff --git a/tests/fix_psycopg.py b/tests/fix_psycopg.py index 74b82b3cb..f036c9857 100644 --- a/tests/fix_psycopg.py +++ b/tests/fix_psycopg.py @@ -9,12 +9,14 @@ def global_adapters(): 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: