Loading django/db/backends/__init__.py +6 −1 Original line number Diff line number Diff line Loading @@ -351,9 +351,14 @@ class BaseDatabaseWrapper(object): def is_usable(self): """ Tests if the database connection is usable. This function may assume that self.connection is not None. Actual implementations should take care not to raise exceptions as that may prevent Django from recycling unusable connections. """ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an is_usable() method') raise NotImplementedError( "subclasses of BaseDatabaseWrapper may require an is_usable() method") def close_if_unusable_or_obsolete(self): """ Loading django/db/backends/mysql/base.py +1 −1 Original line number Diff line number Diff line Loading @@ -552,7 +552,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): def is_usable(self): try: self.connection.ping() except DatabaseError: except Database.Error: return False else: return True Loading django/db/backends/oracle/base.py +1 −1 Original line number Diff line number Diff line Loading @@ -704,7 +704,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): else: # Use a cx_Oracle cursor directly, bypassing Django's utilities. self.connection.cursor().execute("SELECT 1 FROM DUAL") except DatabaseError: except Database.Error: return False else: return True Loading django/db/backends/postgresql_psycopg2/base.py +1 −1 Original line number Diff line number Diff line Loading @@ -210,7 +210,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): try: # Use a psycopg cursor directly, bypassing Django's utilities. self.connection.cursor().execute("SELECT 1") except DatabaseError: except Database.Error: return False else: return True Loading tests/backends/tests.py +31 −0 Original line number Diff line number Diff line Loading @@ -665,6 +665,37 @@ class BackendTestCase(TestCase): self.assertTrue(cursor.closed) class IsUsableTests(TransactionTestCase): # Avoid using a regular TestCase because Django really dislikes closing # the database connection inside a transaction at this point (#21202). available_apps = [] # Unfortunately with sqlite3 the in-memory test database cannot be closed. @skipUnlessDBFeature('test_db_allows_multiple_connections') def test_is_usable_after_database_disconnects(self): """ Test that is_usable() doesn't crash when the database disconnects. Regression for #21553. """ # Open a connection to the database. with connection.cursor(): pass # Emulate a connection close by the database. connection._close() # Even then is_usable() should not raise an exception. try: self.assertFalse(connection.is_usable()) finally: # Clean up the mess created by connection._close(). Since the # connection is already closed, this crashes on some backends. try: connection.close() except Exception: pass # We don't make these tests conditional because that means we would need to # check and differentiate between: # * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do). Loading Loading
django/db/backends/__init__.py +6 −1 Original line number Diff line number Diff line Loading @@ -351,9 +351,14 @@ class BaseDatabaseWrapper(object): def is_usable(self): """ Tests if the database connection is usable. This function may assume that self.connection is not None. Actual implementations should take care not to raise exceptions as that may prevent Django from recycling unusable connections. """ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an is_usable() method') raise NotImplementedError( "subclasses of BaseDatabaseWrapper may require an is_usable() method") def close_if_unusable_or_obsolete(self): """ Loading
django/db/backends/mysql/base.py +1 −1 Original line number Diff line number Diff line Loading @@ -552,7 +552,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): def is_usable(self): try: self.connection.ping() except DatabaseError: except Database.Error: return False else: return True Loading
django/db/backends/oracle/base.py +1 −1 Original line number Diff line number Diff line Loading @@ -704,7 +704,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): else: # Use a cx_Oracle cursor directly, bypassing Django's utilities. self.connection.cursor().execute("SELECT 1 FROM DUAL") except DatabaseError: except Database.Error: return False else: return True Loading
django/db/backends/postgresql_psycopg2/base.py +1 −1 Original line number Diff line number Diff line Loading @@ -210,7 +210,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): try: # Use a psycopg cursor directly, bypassing Django's utilities. self.connection.cursor().execute("SELECT 1") except DatabaseError: except Database.Error: return False else: return True Loading
tests/backends/tests.py +31 −0 Original line number Diff line number Diff line Loading @@ -665,6 +665,37 @@ class BackendTestCase(TestCase): self.assertTrue(cursor.closed) class IsUsableTests(TransactionTestCase): # Avoid using a regular TestCase because Django really dislikes closing # the database connection inside a transaction at this point (#21202). available_apps = [] # Unfortunately with sqlite3 the in-memory test database cannot be closed. @skipUnlessDBFeature('test_db_allows_multiple_connections') def test_is_usable_after_database_disconnects(self): """ Test that is_usable() doesn't crash when the database disconnects. Regression for #21553. """ # Open a connection to the database. with connection.cursor(): pass # Emulate a connection close by the database. connection._close() # Even then is_usable() should not raise an exception. try: self.assertFalse(connection.is_usable()) finally: # Clean up the mess created by connection._close(). Since the # connection is already closed, this crashes on some backends. try: connection.close() except Exception: pass # We don't make these tests conditional because that means we would need to # check and differentiate between: # * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do). Loading