[1.11.x] Fixed #28091 -- Re-raised original exception when closing cursor cleanup fails

This commit is contained in:
François Freitag 2017-04-18 11:15:42 -07:00 committed by Claude Paroz
parent 5e2bbcd70c
commit 56746fb21f
3 changed files with 25 additions and 2 deletions

View File

@ -874,7 +874,7 @@ class SQLCompiler(object):
cursor = self.connection.cursor() cursor = self.connection.cursor()
try: try:
cursor.execute(sql, params) cursor.execute(sql, params)
except Exception: except Exception as original_exception:
try: try:
# Might fail for server-side cursors (e.g. connection closed) # Might fail for server-side cursors (e.g. connection closed)
cursor.close() cursor.close()
@ -883,7 +883,7 @@ class SQLCompiler(object):
# Python 2 doesn't chain exceptions. Remove this error # Python 2 doesn't chain exceptions. Remove this error
# silencing when dropping Python 2 compatibility. # silencing when dropping Python 2 compatibility.
pass pass
raise raise original_exception
if result_type == CURSOR: if result_type == CURSOR:
# Caller didn't specify a result_type, so just give them back the # Caller didn't specify a result_type, so just give them back the

View File

@ -42,3 +42,7 @@ Bugfixes
``ModelAdmin.radio_fields`` with ``admin.HORIZONTAL`` (:ticket:`28059`). ``ModelAdmin.radio_fields`` with ``admin.HORIZONTAL`` (:ticket:`28059`).
* Fixed crash in ``BaseGeometryWidget.subwidgets()`` (:ticket:`28039`). * 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).

View File

@ -682,6 +682,25 @@ class BackendTestCase(TransactionTestCase):
self.create_squares(args, 'pyformat', multiple=True) self.create_squares(args, 'pyformat', multiple=True)
self.assertEqual(Square.objects.count(), 9) 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): def test_unicode_fetches(self):
# fetchone, fetchmany, fetchall return strings as unicode objects #6254 # fetchone, fetchmany, fetchall return strings as unicode objects #6254
qn = connection.ops.quote_name qn = connection.ops.quote_name