summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorsuraj <suraj.shaw@oracle.com>2024-09-10 20:56:16 +0530
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-12-17 11:26:32 +0100
commit0d9872fc9a70ef6966930c68c68febea7eb60ede (patch)
treeeb61a0623f7c0bceb5eeb05c36839cfa98cb2d64 /django
parent2249370c8611d97f8bdb6003fb7b4d8fd3646202 (diff)
Fixed #7732 -- Added support for connection pools on Oracle.
Diffstat (limited to 'django')
-rw-r--r--django/db/backends/oracle/base.py52
-rw-r--r--django/db/backends/oracle/creation.py15
-rw-r--r--django/db/backends/oracle/features.py19
3 files changed, 80 insertions, 6 deletions
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index df78d9ba27..3b37c38f97 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -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():
diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py
index b0a5177728..682379930f 100644
--- a/django/db/backends/oracle/creation.py
+++ b/django/db/backends/oracle/creation.py
@@ -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:
diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py
index 72c6180f50..ad9ab8da55 100644
--- a/django/db/backends/oracle/features.py
+++ b/django/db/backends/oracle/features.py
@@ -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(
{