Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions django/db/models/sql/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1629,9 +1629,12 @@ def execute_sql(
cursor = self.connection.cursor()
try:
cursor.execute(sql, params)
except Exception:
except Exception as e:
# Might fail for server-side cursors (e.g. connection closed)
cursor.close()
try:
cursor.close()
except DatabaseError:
raise e from None
raise

if result_type == ROW_COUNT:
Expand Down
12 changes: 12 additions & 0 deletions docs/releases/5.2.11.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
===========================
Django 5.2.11 release notes
===========================

*Expected February 3, 2026*

Django 5.2.11 fixes several bugs in 5.2.10.

Bugfixes
========

* ...
12 changes: 12 additions & 0 deletions docs/releases/6.0.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
==========================
Django 6.0.2 release notes
==========================

*Expected February 3, 2026*

Django 6.0.2 fixes several bugs in 6.0.1.

Bugfixes
========

* ...
2 changes: 2 additions & 0 deletions docs/releases/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1

6.0.2
6.0.1
6.0

Expand All @@ -40,6 +41,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1

5.2.11
5.2.10
5.2.9
5.2.8
Expand Down
30 changes: 27 additions & 3 deletions tests/queries/test_sqlcompiler.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from django.db import DEFAULT_DB_ALIAS, connection
from unittest import mock

from django.db import DEFAULT_DB_ALIAS, DatabaseError, connection
from django.db.models.sql import Query
from django.test import SimpleTestCase
from django.db.models.sql.compiler import SQLCompiler
from django.test import TestCase

from .models import Item


class SQLCompilerTest(SimpleTestCase):
class SQLCompilerTest(TestCase):
def test_repr(self):
query = Query(Item)
compiler = query.get_compiler(DEFAULT_DB_ALIAS, connection)
Expand All @@ -15,3 +18,24 @@ def test_repr(self):
f"<DatabaseWrapper vendor={connection.vendor!r} alias='default'> "
f"using='default'>",
)

def test_execute_sql_suppresses_cursor_closing_failure_on_exception(self):
query = Query(Item)
compiler = SQLCompiler(query, connection, None)
cursor = mock.MagicMock()

# When execution fails, the cursor may have been closed.
# Django's attempt to close it again will fail, and needs catching.
execute_err = DatabaseError("execute failed")
cursor.execute.side_effect = execute_err
cursor.close.side_effect = DatabaseError("close failed")

with mock.patch.object(connection, "cursor", return_value=cursor):
with self.assertRaises(DatabaseError) as ctx:
compiler.execute_sql("SELECT 1", [])

# There is no irrelevant context from trying to close a closed cursor.
exc = ctx.exception
self.assertIs(exc, execute_err)
self.assertIsNone(exc.__cause__)
self.assertTrue(exc.__suppress_context__)