summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrançois Freitag <mail@franek.fr>2017-04-18 11:15:42 -0700
committerClaude Paroz <claude@2xlibre.net>2017-04-20 17:43:39 +0200
commit56746fb21f6bbf93fe96f4e3908040111439c9c3 (patch)
tree9bc008d23c692b1d86939bc1517774301f0de707
parent5e2bbcd70c9b0213bfda31b90e4b1c35815880e9 (diff)
[1.11.x] Fixed #28091 -- Re-raised original exception when closing cursor cleanup fails
-rw-r--r--django/db/models/sql/compiler.py4
-rw-r--r--docs/releases/1.11.1.txt4
-rw-r--r--tests/backends/tests.py19
3 files changed, 25 insertions, 2 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 1c6fad4d76..9acb56aa84 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -874,7 +874,7 @@ class SQLCompiler(object):
cursor = self.connection.cursor()
try:
cursor.execute(sql, params)
- except Exception:
+ except Exception as original_exception:
try:
# Might fail for server-side cursors (e.g. connection closed)
cursor.close()
@@ -883,7 +883,7 @@ class SQLCompiler(object):
# Python 2 doesn't chain exceptions. Remove this error
# silencing when dropping Python 2 compatibility.
pass
- raise
+ raise original_exception
if result_type == CURSOR:
# Caller didn't specify a result_type, so just give them back the
diff --git a/docs/releases/1.11.1.txt b/docs/releases/1.11.1.txt
index 1f475ee736..e2f49a3c03 100644
--- a/docs/releases/1.11.1.txt
+++ b/docs/releases/1.11.1.txt
@@ -42,3 +42,7 @@ Bugfixes
``ModelAdmin.radio_fields`` with ``admin.HORIZONTAL`` (:ticket:`28059`).
* Fixed crash in ``BaseGeometryWidget.subwidgets()`` (:ticket:`28039`).
+
+* Re-raised the original exception when ``cursor.execute()`` fails and the
+ cleanup code ``cursor.close()`` fails as well, instead of raising the cleanup
+ code failure (:ticket:28091).
diff --git a/tests/backends/tests.py b/tests/backends/tests.py
index 9b31356bfe..5a49d99217 100644
--- a/tests/backends/tests.py
+++ b/tests/backends/tests.py
@@ -682,6 +682,25 @@ class BackendTestCase(TransactionTestCase):
self.create_squares(args, 'pyformat', multiple=True)
self.assertEqual(Square.objects.count(), 9)
+ def test_cursor_close_failure_does_not_mask_original_exception(self):
+ persons = Person.objects.iterator()
+
+ def raise_original_exception(*args, **kwargs):
+ raise Exception('Real exception raised by the database on cursor.execute')
+
+ def raise_close_exception():
+ raise Exception(
+ 'Error when attempting to close the cursor, this exception should not mask the original exception'
+ )
+
+ mock_cursor = connection.chunked_cursor()
+ mock_cursor.execute = mock.MagicMock(side_effect=raise_original_exception)
+ mock_cursor.close = mock.MagicMock(side_effect=raise_close_exception)
+
+ with self.assertRaisesMessage(Exception, 'Real exception raised by the database on cursor.execute'):
+ with mock.patch('django.db.connection.create_cursor', return_value=mock_cursor):
+ list(persons)
+
def test_unicode_fetches(self):
# fetchone, fetchmany, fetchall return strings as unicode objects #6254
qn = connection.ops.quote_name