]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Minor optimizations to data fetching in the cursor
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 14 Apr 2020 11:11:36 +0000 (23:11 +1200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Wed, 15 Apr 2020 11:35:01 +0000 (23:35 +1200)
Shaved a few percents here and there out of profile indications.

psycopg3/adapt.py
psycopg3/cursor.py
psycopg3/types/composite.py

index 2702b6e37217a32297d57bce5a1e0eb504a73a85..eadfcd1b4e55ebd0578207ecfe18fd89f15aea9b 100644 (file)
@@ -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:
index 13856b50a88313882385695c23e252aa78396348..e0f7e50403761c92cd2ad8f793376ece9f894d00 100644 (file)
@@ -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)
index 1e02609c00fa0d4538f34f40d2e551f07e9a0e86..6602607ba84dfb6dc31e9c4075ab6f73d1abaeed 100644 (file)
@@ -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(