mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #7732 -- Added support for connection pools on Oracle.
This commit is contained in:
@@ -14,6 +14,7 @@ from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import IntegrityError
|
||||
from django.db.backends.base.base import BaseDatabaseWrapper
|
||||
from django.db.backends.oracle.oracledb_any import is_oracledb
|
||||
from django.db.backends.utils import debug_transaction
|
||||
from django.utils.asyncio import async_unsafe
|
||||
from django.utils.encoding import force_bytes, force_str
|
||||
@@ -235,6 +236,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
introspection_class = DatabaseIntrospection
|
||||
ops_class = DatabaseOperations
|
||||
validation_class = DatabaseValidation
|
||||
_connection_pools = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -243,10 +245,52 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
)
|
||||
self.features.can_return_columns_from_insert = use_returning_into
|
||||
|
||||
@property
|
||||
def is_pool(self):
|
||||
return self.settings_dict["OPTIONS"].get("pool", False)
|
||||
|
||||
@property
|
||||
def pool(self):
|
||||
if not self.is_pool:
|
||||
return None
|
||||
|
||||
if self.settings_dict.get("CONN_MAX_AGE", 0) != 0:
|
||||
raise ImproperlyConfigured(
|
||||
"Pooling doesn't support persistent connections."
|
||||
)
|
||||
|
||||
pool_key = (self.alias, self.settings_dict["USER"])
|
||||
if pool_key not in self._connection_pools:
|
||||
connect_kwargs = self.get_connection_params()
|
||||
pool_options = connect_kwargs.pop("pool")
|
||||
if pool_options is not True:
|
||||
connect_kwargs.update(pool_options)
|
||||
|
||||
pool = Database.create_pool(
|
||||
user=self.settings_dict["USER"],
|
||||
password=self.settings_dict["PASSWORD"],
|
||||
dsn=dsn(self.settings_dict),
|
||||
**connect_kwargs,
|
||||
)
|
||||
self._connection_pools.setdefault(pool_key, pool)
|
||||
|
||||
return self._connection_pools[pool_key]
|
||||
|
||||
def close_pool(self):
|
||||
if self.pool:
|
||||
self.pool.close(force=True)
|
||||
pool_key = (self.alias, self.settings_dict["USER"])
|
||||
del self._connection_pools[pool_key]
|
||||
|
||||
def get_database_version(self):
|
||||
return self.oracle_version
|
||||
|
||||
def get_connection_params(self):
|
||||
# Pooling feature is only supported for oracledb.
|
||||
if self.is_pool and not is_oracledb:
|
||||
raise ImproperlyConfigured(
|
||||
"Pooling isn't supported by cx_Oracle. Use python-oracledb instead."
|
||||
)
|
||||
conn_params = self.settings_dict["OPTIONS"].copy()
|
||||
if "use_returning_into" in conn_params:
|
||||
del conn_params["use_returning_into"]
|
||||
@@ -254,6 +298,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
|
||||
@async_unsafe
|
||||
def get_new_connection(self, conn_params):
|
||||
if self.pool:
|
||||
return self.pool.acquire()
|
||||
return Database.connect(
|
||||
user=self.settings_dict["USER"],
|
||||
password=self.settings_dict["PASSWORD"],
|
||||
@@ -345,6 +391,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
else:
|
||||
return True
|
||||
|
||||
def close_if_health_check_failed(self):
|
||||
if self.pool:
|
||||
# The pool only returns healthy connections.
|
||||
return
|
||||
return super().close_if_health_check_failed()
|
||||
|
||||
@cached_property
|
||||
def oracle_version(self):
|
||||
with self.temporary_connection():
|
||||
|
||||
@@ -205,13 +205,15 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||
Destroy a test database, prompting the user for confirmation if the
|
||||
database already exists. Return the name of the test database created.
|
||||
"""
|
||||
self.connection.settings_dict["USER"] = self.connection.settings_dict[
|
||||
"SAVED_USER"
|
||||
]
|
||||
self.connection.settings_dict["PASSWORD"] = self.connection.settings_dict[
|
||||
"SAVED_PASSWORD"
|
||||
]
|
||||
if not self.connection.is_pool:
|
||||
self.connection.settings_dict["USER"] = self.connection.settings_dict[
|
||||
"SAVED_USER"
|
||||
]
|
||||
self.connection.settings_dict["PASSWORD"] = self.connection.settings_dict[
|
||||
"SAVED_PASSWORD"
|
||||
]
|
||||
self.connection.close()
|
||||
self.connection.close_pool()
|
||||
parameters = self._get_test_db_params()
|
||||
with self._maindb_connection.cursor() as cursor:
|
||||
if self._test_user_create():
|
||||
@@ -223,6 +225,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||
self.log("Destroying test database tables...")
|
||||
self._execute_test_db_destruction(cursor, parameters, verbosity)
|
||||
self._maindb_connection.close()
|
||||
self._maindb_connection.close_pool()
|
||||
|
||||
def _execute_test_db_creation(self, cursor, parameters, verbosity, keepdb=False):
|
||||
if verbosity >= 2:
|
||||
|
||||
@@ -139,6 +139,25 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
},
|
||||
}
|
||||
)
|
||||
if self.connection.is_pool:
|
||||
skips.update(
|
||||
{
|
||||
"Pooling does not support persistent connections": {
|
||||
"backends.base.test_base.ConnectionHealthChecksTests."
|
||||
"test_health_checks_enabled",
|
||||
"backends.base.test_base.ConnectionHealthChecksTests."
|
||||
"test_health_checks_enabled_errors_occurred",
|
||||
"backends.base.test_base.ConnectionHealthChecksTests."
|
||||
"test_health_checks_disabled",
|
||||
"backends.base.test_base.ConnectionHealthChecksTests."
|
||||
"test_set_autocommit_health_checks_enabled",
|
||||
"servers.tests.LiveServerTestCloseConnectionTest."
|
||||
"test_closes_connections",
|
||||
"backends.oracle.tests.TransactionalTests."
|
||||
"test_password_with_at_sign",
|
||||
},
|
||||
}
|
||||
)
|
||||
if is_oracledb and self.connection.oracledb_version >= (2, 1, 2):
|
||||
skips.update(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user