mirror of
				https://github.com/django/django.git
				synced 2025-10-30 09:06:13 +00:00 
			
		
		
		
	[1.6.x] Fixed #21553 -- Ensured unusable database connections get closed.
Backport of 5f2f47f from master
			
			
This commit is contained in:
		| @@ -442,9 +442,14 @@ class BaseDatabaseWrapper(object): | |||||||
|     def is_usable(self): |     def is_usable(self): | ||||||
|         """ |         """ | ||||||
|         Tests if the database connection is usable. |         Tests if the database connection is usable. | ||||||
|  |  | ||||||
|         This function may assume that self.connection is not None. |         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 |         raise NotImplementedError( | ||||||
|  |             "subclasses of BaseDatabaseWrapper may require an is_usable() method") | ||||||
|  |  | ||||||
|     def close_if_unusable_or_obsolete(self): |     def close_if_unusable_or_obsolete(self): | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -517,7 +517,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): | |||||||
|     def is_usable(self): |     def is_usable(self): | ||||||
|         try: |         try: | ||||||
|             self.connection.ping() |             self.connection.ping() | ||||||
|         except DatabaseError: |         except Database.Error: | ||||||
|             return False |             return False | ||||||
|         else: |         else: | ||||||
|             return True |             return True | ||||||
|   | |||||||
| @@ -642,7 +642,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): | |||||||
|             else: |             else: | ||||||
|                 # Use a cx_Oracle cursor directly, bypassing Django's utilities. |                 # Use a cx_Oracle cursor directly, bypassing Django's utilities. | ||||||
|                 self.connection.cursor().execute("SELECT 1 FROM DUAL") |                 self.connection.cursor().execute("SELECT 1 FROM DUAL") | ||||||
|         except DatabaseError: |         except Database.Error: | ||||||
|             return False |             return False | ||||||
|         else: |         else: | ||||||
|             return True |             return True | ||||||
|   | |||||||
| @@ -190,7 +190,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): | |||||||
|         try: |         try: | ||||||
|             # Use a psycopg cursor directly, bypassing Django's utilities. |             # Use a psycopg cursor directly, bypassing Django's utilities. | ||||||
|             self.connection.cursor().execute("SELECT 1") |             self.connection.cursor().execute("SELECT 1") | ||||||
|         except DatabaseError: |         except Database.Error: | ||||||
|             return False |             return False | ||||||
|         else: |         else: | ||||||
|             return True |             return True | ||||||
|   | |||||||
| @@ -612,6 +612,37 @@ class BackendTestCase(TestCase): | |||||||
|             cursor.execute(query) |             cursor.execute(query) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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 | # We don't make these tests conditional because that means we would need to | ||||||
| # check and differentiate between: | # check and differentiate between: | ||||||
| # * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do). | # * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do). | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user