From: Daniele Varrazzo Date: Tue, 14 Apr 2020 11:11:36 +0000 (+1200) Subject: Minor optimizations to data fetching in the cursor X-Git-Tag: 3.0.dev0~557 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3bafd097eb982d27b7dd593896710e3219c9c583;p=thirdparty%2Fpsycopg.git Minor optimizations to data fetching in the cursor Shaved a few percents here and there out of profile indications. --- diff --git a/psycopg3/adapt.py b/psycopg3/adapt.py index 2702b6e37..eadfcd1b4 100644 --- a/psycopg3/adapt.py +++ b/psycopg3/adapt.py @@ -5,7 +5,7 @@ Entry point into the adaptation system. # Copyright (C) 2020 The Psycopg Team import codecs -from typing import Any, Callable, Dict, Generator, Iterable, List, Optional +from typing import Any, Callable, Dict, Iterable, List, Optional from typing import Tuple, Type, Union from . import errors as e @@ -289,12 +289,11 @@ class Transformer: def load_sequence( self, record: Iterable[Optional[bytes]] - ) -> Generator[Any, None, None]: - for val, loader in zip(record, self._row_loaders): - if val is not None: - yield loader(val) - else: - yield None + ) -> Tuple[Any, ...]: + return tuple( + (self._row_loaders[i](val) if val is not None else None) + for i, val in enumerate(record) + ) def load(self, data: bytes, oid: int, format: Format = Format.TEXT) -> Any: if data is not None: diff --git a/psycopg3/cursor.py b/psycopg3/cursor.py index 13856b50a..e0f7e5040 100644 --- a/psycopg3/cursor.py +++ b/psycopg3/cursor.py @@ -96,11 +96,14 @@ class BaseCursor: @pgresult.setter def pgresult(self, result: Optional[pq.PGresult]) -> None: self._pgresult = result - if result is not None and self._transformer is not None: - self._transformer.set_row_types( - (result.ftype(i), result.fformat(i)) - for i in range(result.nfields) - ) + if result is not None: + self._ntuples = result.ntuples + self._nfields = result.nfields + if self._transformer is not None: + self._transformer.set_row_types( + (result.ftype(i), result.fformat(i)) + for i in range(self._nfields) + ) @property def description(self) -> Optional[List[Column]]: @@ -231,7 +234,7 @@ class BaseCursor: else: return None - def _load_row(self, n: int) -> Optional[Tuple[Any, ...]]: + def _check_result(self) -> None: res = self.pgresult if res is None: raise e.ProgrammingError("no result available") @@ -240,13 +243,13 @@ class BaseCursor: "the last operation didn't produce a result" ) - if n >= res.ntuples: + def _load_row(self, n: int) -> Optional[Tuple[Any, ...]]: + if n >= self._ntuples: return None - return tuple( - self._transformer.load_sequence( - res.get_value(n, i) for i in range(res.nfields) - ) + get_value = self.pgresult.get_value # type: ignore + return self._transformer.load_sequence( + get_value(n, i) for i in range(self._nfields) ) @@ -292,12 +295,14 @@ class Cursor(BaseCursor): return self def fetchone(self) -> Optional[Sequence[Any]]: + self._check_result() rv = self._load_row(self._pos) if rv is not None: self._pos += 1 return rv def fetchmany(self, size: Optional[int] = None) -> List[Sequence[Any]]: + self._check_result() if size is None: size = self.arraysize @@ -312,6 +317,7 @@ class Cursor(BaseCursor): return rv def fetchall(self) -> List[Sequence[Any]]: + self._check_result() rv: List[Sequence[Any]] = [] while 1: row = self._load_row(self._pos) @@ -367,6 +373,7 @@ class AsyncCursor(BaseCursor): return self async def fetchone(self) -> Optional[Sequence[Any]]: + self._check_result() rv = self._load_row(self._pos) if rv is not None: self._pos += 1 @@ -375,6 +382,7 @@ class AsyncCursor(BaseCursor): async def fetchmany( self, size: Optional[int] = None ) -> List[Sequence[Any]]: + self._check_result() if size is None: size = self.arraysize @@ -389,6 +397,7 @@ class AsyncCursor(BaseCursor): return rv async def fetchall(self) -> List[Sequence[Any]]: + self._check_result() rv: List[Sequence[Any]] = [] while 1: row = self._load_row(self._pos) diff --git a/psycopg3/types/composite.py b/psycopg3/types/composite.py index 1e02609c0..6602607ba 100644 --- a/psycopg3/types/composite.py +++ b/psycopg3/types/composite.py @@ -215,11 +215,9 @@ class BinaryRecordLoader(BaseCompositeLoader): self._config_types(data) self._types_set = True - return tuple( - self._tx.load_sequence( - data[offset : offset + length] if length != -1 else None - for _, offset, length in self._walk_record(data) - ) + return self._tx.load_sequence( + data[offset : offset + length] if length != -1 else None + for _, offset, length in self._walk_record(data) ) def _walk_record(