Ticket #19707: ticket_19707-2.diff
File ticket_19707-2.diff, 10.9 KB (added by , 12 years ago) |
---|
-
django/db/__init__.py
diff --git a/django/db/__init__.py b/django/db/__init__.py index b198048..dc9b34c 100644
a b backend = load_backend(connection.settings_dict['ENGINE']) 42 42 # Register an event that closes the database connection 43 43 # when a Django request is finished. 44 44 def close_connection(**kwargs): 45 for conn in connections.all(): 46 conn.close() 45 # Avoid circular imports 46 from django.db import transaction 47 for conn in connections: 48 try: 49 transaction.abort(conn) 50 connections[conn].close() 51 except: 52 # The connection's state is unknown, so it has to be 53 # abandoned. This could happen for example if the network 54 # connection has a failure. 55 del connections[conn] 47 56 signals.request_finished.connect(close_connection) 48 57 49 58 # Register an event that resets connection.queries -
django/db/backends/__init__.py
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 7dc5456..bbb5a5b 100644
a b class BaseDatabaseWrapper(object): 88 88 return 89 89 self.cursor().execute(self.ops.savepoint_commit_sql(sid)) 90 90 91 def abort(self): 92 """ 93 Roll back any ongoing transaction and clean the transaction state 94 stack. 95 """ 96 if self._dirty: 97 self._rollback() 98 self._dirty = False 99 while self.transaction_state: 100 self.leave_transaction_management() 101 91 102 def enter_transaction_management(self, managed=True): 92 103 """ 93 104 Enters transaction management for a running thread. It must be balanced with -
django/db/transaction.py
diff --git a/django/db/transaction.py b/django/db/transaction.py index f3ce2b2..dd7e2f4 100644
a b class TransactionManagementError(Exception): 24 24 """ 25 25 pass 26 26 27 def abort(using=None): 28 """ 29 Roll back any ongoing transactions and clean the transaction management 30 state of the connection. 31 32 This method is to be used only in cases where using balanced 33 leave_transaction_management() calls isn't possible. For example after a 34 request has finished, the transaction state isn't known, yet the connection 35 must be cleaned up for the next request. 36 """ 37 if using is None: 38 using = DEFAULT_DB_ALIAS 39 connection = connections[using] 40 connection.abort() 41 27 42 def enter_transaction_management(managed=True, using=None): 28 43 """ 29 44 Enters transaction management for a running thread. It must be balanced with -
django/db/utils.py
diff --git a/django/db/utils.py b/django/db/utils.py index 842fd35..fb9bdc1 100644
a b class ConnectionHandler(object): 98 98 def __setitem__(self, key, value): 99 99 setattr(self._connections, key, value) 100 100 101 def __delitem__(self, key): 102 delattr(self._connections, key) 103 101 104 def __iter__(self): 102 105 return iter(self.databases) 103 106 -
django/middleware/transaction.py
diff --git a/django/middleware/transaction.py b/django/middleware/transaction.py index 96b1538..7888839 100644
a b class TransactionMiddleware(object): 15 15 def process_exception(self, request, exception): 16 16 """Rolls back the database and leaves transaction management""" 17 17 if transaction.is_dirty(): 18 # This rollback might fail for closed connections for example. 18 19 transaction.rollback() 19 20 transaction.leave_transaction_management() 20 21 … … class TransactionMiddleware(object): 22 23 """Commits and leaves transaction management.""" 23 24 if transaction.is_managed(): 24 25 if transaction.is_dirty(): 25 transaction.commit() 26 # Note: it is possible that the commit fails. If the reason is 27 # closed connection or some similar reason, then there is 28 # little hope to proceed nicely. However, in case of deferred 29 # foreign key check failures we can still rollback(). In any 30 # case we want to have an informative exception raised. 31 try: 32 transaction.commit() 33 except: 34 # If the rollback fails, the transaction state will be 35 # messed up. It doesn't matter, the connection will be set 36 # to clean state after the request finishes. And, we can't 37 # clean the state here properly even if we wanted to, the 38 # connection is in transaction but we can't rollback... 39 transaction.rollback() 40 transaction.leave_transaction_management() 41 raise 26 42 transaction.leave_transaction_management() 27 43 return response -
django/test/testcases.py
diff --git a/django/test/testcases.py b/django/test/testcases.py index 3aa0afa..f7c34a9 100644
a b real_rollback = transaction.rollback 70 70 real_enter_transaction_management = transaction.enter_transaction_management 71 71 real_leave_transaction_management = transaction.leave_transaction_management 72 72 real_managed = transaction.managed 73 real_abort = transaction.abort 73 74 74 75 def nop(*args, **kwargs): 75 76 return … … def disable_transaction_methods(): 80 81 transaction.enter_transaction_management = nop 81 82 transaction.leave_transaction_management = nop 82 83 transaction.managed = nop 84 transaction.abort = nop 83 85 84 86 def restore_transaction_methods(): 85 87 transaction.commit = real_commit … … def restore_transaction_methods(): 87 89 transaction.enter_transaction_management = real_enter_transaction_management 88 90 transaction.leave_transaction_management = real_leave_transaction_management 89 91 transaction.managed = real_managed 92 transaction.abort = real_abort 90 93 91 94 92 95 def assert_and_parse_html(self, html, user_msg, msg): -
tests/regressiontests/middleware/tests.py
diff --git a/tests/regressiontests/middleware/tests.py b/tests/regressiontests/middleware/tests.py index a9a45c9..6c43641 100644
a b import warnings 9 9 10 10 from django.conf import settings 11 11 from django.core import mail 12 from django.db import transaction13 from django.http import HttpRequest 14 from django.http import HttpRe sponse, StreamingHttpResponse12 from django.db import (transaction, connections, DEFAULT_DB_ALIAS, 13 IntegrityError) 14 from django.http import HttpRequest, HttpResponse, StreamingHttpResponse 15 15 from django.middleware.clickjacking import XFrameOptionsMiddleware 16 16 from django.middleware.common import CommonMiddleware, BrokenLinkEmailsMiddleware 17 17 from django.middleware.http import ConditionalGetMiddleware … … class TransactionMiddlewareTest(TransactionTestCase): 710 710 TransactionMiddleware().process_exception(self.request, None) 711 711 self.assertEqual(Band.objects.count(), 0) 712 712 self.assertFalse(transaction.is_dirty()) 713 714 def test_failing_commit(self): 715 # It is possible that connection.commit() fails. Check that 716 # TransactionMiddleware handles such cases correctly. 717 try: 718 def raise_exception(): 719 raise IntegrityError() 720 connections[DEFAULT_DB_ALIAS].commit = raise_exception 721 transaction.enter_transaction_management() 722 transaction.managed(True) 723 Band.objects.create(name='The Beatles') 724 self.assertTrue(transaction.is_dirty()) 725 with self.assertRaises(IntegrityError): 726 TransactionMiddleware().process_response(self.request, None) 727 self.assertEqual(Band.objects.count(), 0) 728 self.assertFalse(transaction.is_dirty()) 729 self.assertFalse(transaction.is_managed()) 730 finally: 731 del connections[DEFAULT_DB_ALIAS].commit -
tests/regressiontests/requests/tests.py
diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py index 799cd9b..bcd6ade 100644
a b import warnings 6 6 from datetime import datetime, timedelta 7 7 from io import BytesIO 8 8 9 from django.db import connection, connections, DEFAULT_DB_ALIAS 10 from django.core import signals 9 11 from django.core.exceptions import SuspiciousOperation 10 12 from django.core.handlers.wsgi import WSGIRequest, LimitedStream 11 13 from django.http import HttpRequest, HttpResponse, parse_cookie, build_request_repr, UnreadablePostError 14 from django.test import TransactionTestCase 12 15 from django.test.client import FakePayload 13 16 from django.test.utils import override_settings, str_prefix 14 17 from django.utils import six … … class RequestsTests(unittest.TestCase): 524 527 525 528 with self.assertRaises(UnreadablePostError): 526 529 request.body 530 531 class TransactionRequestTests(TransactionTestCase): 532 def test_request_finished_db_state(self): 533 # The GET below will not succeed, but it will give a response with 534 # defined ._handler_class. That is needed for sending the request_finished 535 # signal. However, the test client itself will not send request_finished 536 # signals. 537 response = self.client.get('/') 538 # Taking a cursor will open a new connection. 539 connection.cursor() 540 connection.enter_transaction_management() 541 connection.managed(True) 542 signals.request_finished.send(sender=response._handler_class) 543 # in-memory sqlite doesn't actually close connections when connections are closed. 544 if connection.vendor != 'sqlite': 545 self.assertIs(connection.connection, None) 546 # And the transaction state is reset. 547 self.assertEqual(len(connection.transaction_state), 0) 548 549 @unittest.skipIf(connection.vendor == 'sqlite', 550 'This test will close the connection which must not be done with ' 551 'in-memory sqlite') 552 def test_request_finished_failed_connection(self): 553 # See comments in test_request_finished_db_state() for the self.client 554 # usage. 555 response = self.client.get('/') 556 557 conn = connections[DEFAULT_DB_ALIAS] 558 conn.enter_transaction_management() 559 conn.managed(True) 560 conn.set_dirty() 561 562 # Test that the rollback doesn't succeed (for example network failure 563 # could cause this). 564 def fail_horribly(): 565 raise Exception("Horrible failure!") 566 conn._rollback = fail_horribly 567 568 signals.request_finished.send(sender=response._handler_class) 569 # As even rollback wasn't possible the connection wrapper itself was 570 # abandoned. 571 self.assertIsNot(conn, connections[DEFAULT_DB_ALIAS]) 572 # And for the connection (that is, django.db.connection) that it has 573 # its transaction state reset. 574 self.assertEqual(len(connection.transaction_state), 0)