From: Daniele Varrazzo Date: Thu, 6 Aug 2020 01:09:35 +0000 (+0100) Subject: Added Transformer.types_sequence X-Git-Tag: 3.0.dev0~458^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c95f939244eff1a94051833eaccfe85915fa9a3b;p=thirdparty%2Fpsycopg.git Added Transformer.types_sequence Not entirely sure yet if it will be needed, but this allows to free dump_sequence() from returning the oids as second argument. Dropped Transformer.get_dump_function() too. --- diff --git a/psycopg3/psycopg3/proto.py b/psycopg3/psycopg3/proto.py index 9b84e28d4..9ae326aad 100644 --- a/psycopg3/psycopg3/proto.py +++ b/psycopg3/psycopg3/proto.py @@ -82,10 +82,10 @@ class Transformer(Protocol): ) -> Tuple[List[Optional[bytes]], List[int]]: ... - def dump(self, obj: None, format: Format = Format.TEXT) -> MaybeOid: + def types_sequence(self) -> List[int]: ... - def get_dump_function(self, src: type, format: Format) -> DumpFunc: + def dump(self, obj: None, format: Format = Format.TEXT) -> MaybeOid: ... def lookup_dumper(self, src: type, format: Format) -> DumperType: diff --git a/psycopg3/psycopg3/transform.py b/psycopg3/psycopg3/transform.py index e8a1c714c..95c714082 100644 --- a/psycopg3/psycopg3/transform.py +++ b/psycopg3/psycopg3/transform.py @@ -6,15 +6,19 @@ Helper object to transform values between Python and PostgreSQL import codecs from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple +from typing import TYPE_CHECKING from . import errors as e from . import pq -from .proto import AdaptContext, DumpFunc, DumpersMap, DumperType +from .proto import AdaptContext, DumpersMap, DumperType from .proto import LoadFunc, LoadersMap, LoaderType, MaybeOid from .cursor import BaseCursor from .connection import BaseConnection from .types.oids import builtins, INVALID_OID +if TYPE_CHECKING: + from .adapt import Dumper + Format = pq.Format TEXT_OID = builtins["text"].oid @@ -36,8 +40,10 @@ class Transformer: self._setup_context(context) self.pgresult = None - # mapping class, fmt -> dump function - self._dump_funcs: Dict[Tuple[type, Format], DumpFunc] = {} + # mapping class, fmt -> Dumper instance + self._dumpers_cache: Dict[Tuple[type, Format], "Dumper"] = {} + + self._oids: List[int] = [] # mapping oid, fmt -> load function self._load_funcs: Dict[Tuple[int, Format], LoadFunc] = {} @@ -136,46 +142,50 @@ class Transformer: for oid, fmt in types: rc.append(self.get_load_function(oid, fmt)) + # TODO: drop? def dump_sequence( self, objs: Iterable[Any], formats: Iterable[Format] ) -> Tuple[List[Optional[bytes]], List[int]]: - out = [] - types = [] + out: List[Optional[bytes]] = [] + oids = self._oids = [] for var, fmt in zip(objs, formats): - data = self.dump(var, fmt) - if isinstance(data, tuple): - oid = data[1] - data = data[0] + if var is not None: + dumper = self.get_dumper(type(var), fmt) + data = dumper.dump(var) + if isinstance(data, tuple): + data = data[0] + + out.append(data) + oids.append(dumper.oid) else: - oid = TEXT_OID + out.append(None) + oids.append(TEXT_OID) - out.append(data) - types.append(oid) + return out, self._oids - return out, types + # TODO: drop? + def types_sequence(self) -> List[int]: + return self._oids + # TODO: drop? def dump(self, obj: None, format: Format = Format.TEXT) -> MaybeOid: if obj is None: return None, TEXT_OID - src = type(obj) - func = self.get_dump_function(src, format) - return func(obj) + dumper = self.get_dumper(type(obj), format) + return dumper.dump(obj) - def get_dump_function(self, src: type, format: Format) -> DumpFunc: + def get_dumper(self, src: type, format: Format) -> "Dumper": key = (src, format) try: - return self._dump_funcs[key] + return self._dumpers_cache[key] except KeyError: pass - dumper = self.lookup_dumper(src, format) - func: DumpFunc - func = dumper(src, self).dump - - self._dump_funcs[key] = func - return func + dumper_cls = self.lookup_dumper(src, format) + self._dumpers_cache[key] = dumper = dumper_cls(src, self) + return dumper def lookup_dumper(self, src: type, format: Format) -> DumperType: key = (src, format) diff --git a/psycopg3/psycopg3/utils/queries.py b/psycopg3/psycopg3/utils/queries.py index 3b0550cf3..c2625ddcb 100644 --- a/psycopg3/psycopg3/utils/queries.py +++ b/psycopg3/psycopg3/utils/queries.py @@ -69,9 +69,9 @@ class PostgresQuery: params = _validate_and_reorder_params( self._parts, vars, self._order ) - self.params, self.types = self._tx.dump_sequence( - params, self.formats or () - ) + self.params, _ = self._tx.dump_sequence(params, self.formats or ()) + if self.types is None: + self.types = self._tx.types_sequence() else: self.params = self.types = None diff --git a/psycopg3_c/psycopg3_c/_psycopg3.pyi b/psycopg3_c/psycopg3_c/_psycopg3.pyi index aaa298834..1a9e1d596 100644 --- a/psycopg3_c/psycopg3_c/_psycopg3.pyi +++ b/psycopg3_c/psycopg3_c/_psycopg3.pyi @@ -35,10 +35,10 @@ class Transformer: def dump_sequence( self, objs: Iterable[Any], formats: Iterable[pq.Format] ) -> Tuple[List[Optional[bytes]], List[int]]: ... + def types_sequence(self) -> List[int]: ... def dump( self, obj: None, format: pq.Format = pq.Format.TEXT ) -> MaybeOid: ... - def get_dump_function(self, src: type, format: pq.Format) -> DumpFunc: ... def lookup_dumper(self, src: type, format: pq.Format) -> DumperType: ... def load_row(self, row: int) -> Optional[Tuple[Any, ...]]: ... def load_sequence( diff --git a/psycopg3_c/psycopg3_c/transform.pyx b/psycopg3_c/psycopg3_c/transform.pyx index 7b438fb6c..adae62a7b 100644 --- a/psycopg3_c/psycopg3_c/transform.pyx +++ b/psycopg3_c/psycopg3_c/transform.pyx @@ -42,8 +42,8 @@ cdef class Transformer: state so adapting several values of the same type can use optimisations. """ - cdef list _dumpers_maps, _loaders_maps - cdef dict _dumpers, _loaders, _dump_funcs, _load_funcs + cdef list _dumpers_maps, _loaders_maps, _oids + cdef dict _dumpers, _loaders, _dumpers_cache, _load_funcs cdef object _connection, _codec cdef PGresult _pgresult cdef int _nfields, _ntuples @@ -57,8 +57,10 @@ cdef class Transformer: self._loaders_maps: List["LoadersMap"] = [] self._setup_context(context) - # mapping class, fmt -> dump function - self._dump_funcs: Dict[Tuple[type, Format], "DumpFunc"] = {} + # mapping class, fmt -> Dumper instance + self._dumpers_cache: Dict[Tuple[type, Format], "Dumper"] = {} + + self._oids: List[int] = [] # mapping oid, fmt -> load function self._load_funcs: Dict[Tuple[int, Format], "LoadFunc"] = {} @@ -220,41 +222,44 @@ cdef class Transformer: def dump_sequence( self, objs: Iterable[Any], formats: Iterable[Format] ) -> Tuple[List[Optional[bytes]], List[int]]: - out = [] - types = [] + out: List[Optional[bytes]] = [] + oids = self._oids = [] for var, fmt in zip(objs, formats): - data = self.dump(var, fmt) - if isinstance(data, tuple): - oid = data[1] - data = data[0] + if var is not None: + dumper = self.get_dumper(type(var), fmt) + data = dumper.dump(var) + if isinstance(data, tuple): + data = data[0] + + out.append(data) + oids.append(dumper.oid) else: - oid = TEXT_OID + out.append(None) + oids.append(TEXT_OID) - out.append(data) - types.append(oid) + return out, oids - return out, types + def types_sequence(self) -> List[int]: + return self._oids def dump(self, obj: None, format: Format = 0) -> "MaybeOid": if obj is None: return None, TEXT_OID - src = type(obj) - func = self.get_dump_function(src, format) - return func(obj) + dumper = self.get_dumper(type(obj), format) + return dumper.dump(obj) - def get_dump_function(self, src: type, format: Format) -> "DumpFunc": + def get_dumper(self, src: type, format: Format) -> "Dumper": key = (src, format) try: - return self._dump_funcs[key] + return self._dumpers_cache[key] except KeyError: pass - dumper = self.lookup_dumper(src, format) - func = dumper(src, self).dump - self._dump_funcs[key] = func - return func + dumper_cls = self.lookup_dumper(src, format) + self._dumpers_cache[key] = dumper = dumper_cls(src, self) + return dumper def lookup_dumper(self, src: type, format: Format) -> "DumperType": key = (src, format)