From 56746fb21f6bbf93fe96f4e3908040111439c9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Tue, 18 Apr 2017 11:15:42 -0700 Subject: [PATCH] [1.11.x] Fixed #28091 -- Re-raised original exception when closing cursor cleanup fails --- django/db/models/sql/compiler.py | 4 ++-- docs/releases/1.11.1.txt | 4 ++++ tests/backends/tests.py | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 1c6fad4d761..9acb56aa844 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 1f475ee7360..e2f49a3c033 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 9b31356bfe5..5a49d992176 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