]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Implement fetch* not in term of iter
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 25 Dec 2020 12:14:34 +0000 (13:14 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 25 Dec 2020 12:14:34 +0000 (13:14 +0100)
Shave some time on lists reallocation, but especially avoid going
through __aiter__ on async, because profiling showed some 20% of the
time spent there.

This makes me rethink the entire giving async interface for things that are
already on the client side. Feels like that, in order to implement server-side
cursor, we are shedding speed on the common use of async connections.

psycopg3/psycopg3/_transform.py
psycopg3/psycopg3/cursor.py

index 36fe978b221b42426e3a885c0be0678bd3bfa8cc..722f719ef7648818f5f9706c86ca49f174904dbc 100644 (file)
@@ -191,13 +191,11 @@ class Transformer:
         if row >= self._ntuples:
             return None
 
-        rv: List[Any] = []
+        rv: List[Any] = [None] * self._nfields
         for col in range(self._nfields):
             val = res.get_value(row, col)
-            if val is None:
-                rv.append(None)
-            else:
-                rv.append(self._row_loaders[col](val))
+            if val is not None:
+                rv[col] = self._row_loaders[col](val)
 
         return tuple(rv)
 
index c5018e617d6e283dd426258d12f5d64d2d3f9e6c..e81f15001f94d26064ba1d08ee4659bd5971bec9 100644 (file)
@@ -514,25 +514,31 @@ class Cursor(BaseCursor["Connection"]):
         if not size:
             size = self.arraysize
 
-        pos = self._pos
+        assert self.pgresult
         load = self._transformer.load_row
-        rv: List[Sequence[Any]] = []
+        rv: List[Any] = [None] * (min(size, self.pgresult.ntuples - self._pos))
 
-        for _ in range(size):
-            row = load(pos)
-            if row is None:
-                break
-            pos += 1
-            rv.append(row)
+        for i in range(len(rv)):
+            rv[i] = load(i + self._pos)
 
-        self._pos = pos
+        self._pos += len(rv)
         return rv
 
     def fetchall(self) -> List[Sequence[Any]]:
         """
         Return all the remaining records from the current recordset.
         """
-        return list(self)
+        self._check_result()
+
+        assert self.pgresult
+        load = self._transformer.load_row
+
+        rv: List[Any] = [None] * (self.pgresult.ntuples - self._pos)
+        for i in range(len(rv)):
+            rv[i] = load(i + self._pos)
+
+        self._pos += len(rv)
+        return rv
 
     def __iter__(self) -> Iterator[Sequence[Any]]:
         self._check_result()
@@ -606,26 +612,28 @@ class AsyncCursor(BaseCursor["AsyncConnection"]):
         if not size:
             size = self.arraysize
 
-        pos = self._pos
+        assert self.pgresult
         load = self._transformer.load_row
-        rv: List[Sequence[Any]] = []
+        rv: List[Any] = [None] * (min(size, self.pgresult.ntuples - self._pos))
 
-        for _ in range(size):
-            row = load(pos)
-            if row is None:
-                break
-            pos += 1
-            rv.append(row)
+        for i in range(len(rv)):
+            rv[i] = load(i + self._pos)
 
-        self._pos = pos
+        self._pos += len(rv)
         return rv
 
     async def fetchall(self) -> List[Sequence[Any]]:
-        res = []
-        async for rec in self:
-            res.append(rec)
+        self._check_result()
 
-        return res
+        assert self.pgresult
+        load = self._transformer.load_row
+
+        rv: List[Any] = [None] * (self.pgresult.ntuples - self._pos)
+        for i in range(len(rv)):
+            rv[i] = load(i + self._pos)
+
+        self._pos += len(rv)
+        return rv
 
     async def __aiter__(self) -> AsyncIterator[Sequence[Any]]:
         self._check_result()