From 7e62e3a00b31e3b645c00d005043bdac920f9d92 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Fri, 25 Dec 2020 13:14:34 +0100 Subject: [PATCH] Implement fetch* not in term of iter 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 | 8 ++--- psycopg3/psycopg3/cursor.py | 54 +++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/psycopg3/psycopg3/_transform.py b/psycopg3/psycopg3/_transform.py index 36fe978b2..722f719ef 100644 --- a/psycopg3/psycopg3/_transform.py +++ b/psycopg3/psycopg3/_transform.py @@ -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) diff --git a/psycopg3/psycopg3/cursor.py b/psycopg3/psycopg3/cursor.py index c5018e617..e81f15001 100644 --- a/psycopg3/psycopg3/cursor.py +++ b/psycopg3/psycopg3/cursor.py @@ -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() -- 2.47.2