]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Fix idempotence of server side cursors close()
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 25 Jul 2021 00:22:55 +0000 (02:22 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 25 Jul 2021 01:37:27 +0000 (03:37 +0200)
psycopg/psycopg/server_cursor.py
tests/test_server_cursor.py
tests/test_server_cursor_async.py

index 201278f78651a89ed76d11f2ef30890250220e47..ec185933014d5538b1feb6e62101349c24d5ec57 100644 (file)
@@ -46,7 +46,7 @@ class ServerCursorHelper(Generic[ConnectionType, Row]):
     def _repr(self, cur: BaseCursor[ConnectionType, Row]) -> str:
         cls = f"{cur.__class__.__module__}.{cur.__class__.__qualname__}"
         info = pq.misc.connection_summary(cur._conn.pgconn)
-        if cur._closed:
+        if cur.closed:
             status = "closed"
         elif not cur.pgresult:
             status = "no result"
@@ -194,7 +194,7 @@ class ServerCursor(BaseCursor["Connection[Any]", Row]):
         self.itersize: int = DEFAULT_ITERSIZE
 
     def __del__(self) -> None:
-        if not self._closed:
+        if not self.closed:
             warnings.warn(
                 f"the server-side cursor {self} was deleted while still open."
                 f" Please use 'with' or '.close()' to close the cursor properly",
@@ -242,8 +242,10 @@ class ServerCursor(BaseCursor["Connection[Any]", Row]):
         Close the current cursor and free associated resources.
         """
         with self._conn.lock:
+            if self.closed:
+                return
             self._conn.wait(self._helper._close_gen(self))
-        self._close()
+            self._close()
 
     def execute(
         self,
@@ -328,7 +330,7 @@ class AsyncServerCursor(BaseCursor["AsyncConnection[Any]", Row]):
         self.itersize: int = DEFAULT_ITERSIZE
 
     def __del__(self) -> None:
-        if not self._closed:
+        if not self.closed:
             warnings.warn(
                 f"the server-side cursor {self} was deleted while still open."
                 f" Please use 'with' or '.close()' to close the cursor properly",
@@ -363,8 +365,10 @@ class AsyncServerCursor(BaseCursor["AsyncConnection[Any]", Row]):
 
     async def close(self) -> None:
         async with self._conn.lock:
+            if self.closed:
+                return
             await self._conn.wait(self._helper._close_gen(self))
-        self._close()
+            self._close()
 
     async def execute(
         self,
index 57c58b9a70313ed4de3493370689df46a163adf3..ecd61e9e84e21013b906b4f0b1d0d5514eff1157 100644 (file)
@@ -67,6 +67,14 @@ def test_close(conn, recwarn, retries):
             assert not recwarn, [str(w.message) for w in recwarn.list]
 
 
+def test_close_idempotent(conn):
+    cur = conn.cursor("foo")
+    cur.execute("select 1")
+    cur.fetchall()
+    cur.close()
+    cur.close()
+
+
 def test_close_noop(conn, recwarn, retries):
     for retry in retries:
         with retry:
index 996c70a5c749ef7d73cb22c6db4f3c0dacf66528..af85b68e9731e4f62d37830fa956bd1501d32c7f 100644 (file)
@@ -71,6 +71,14 @@ async def test_close(aconn, recwarn, retries):
             assert not recwarn, [str(w.message) for w in recwarn.list]
 
 
+async def test_close_idempotent(aconn):
+    cur = aconn.cursor("foo")
+    await cur.execute("select 1")
+    await cur.fetchall()
+    await cur.close()
+    await cur.close()
+
+
 async def test_close_noop(aconn, recwarn, retries):
     async for retry in retries:
         with retry: