mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Fixed #35688 -- Restored timezone and role setters to be PostgreSQL DatabaseWrapper methods.
Following the addition of PostgreSQL connection pool support in
Refs #33497, the methods for configuring the database role and timezone
were moved to module-level functions. This change prevented subclasses
of DatabaseWrapper from overriding these methods as needed, for example,
when creating wrappers for other PostgreSQL-based backends.
Thank you Christian Hardenberg for the report and to
Florian Apolloner and Natalia Bidart for the review.
Regression in fad334e1a9
.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
This commit is contained in:
parent
26a67943ac
commit
7380ac5734
@ -86,24 +86,6 @@ def _get_varchar_column(data):
|
||||
return "varchar(%(max_length)s)" % data
|
||||
|
||||
|
||||
def ensure_timezone(connection, ops, timezone_name):
|
||||
conn_timezone_name = connection.info.parameter_status("TimeZone")
|
||||
if timezone_name and conn_timezone_name != timezone_name:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(ops.set_time_zone_sql(), [timezone_name])
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def ensure_role(connection, ops, role_name):
|
||||
if role_name:
|
||||
with connection.cursor() as cursor:
|
||||
sql = ops.compose_sql("SET ROLE %s", [role_name])
|
||||
cursor.execute(sql)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
vendor = "postgresql"
|
||||
display_name = "PostgreSQL"
|
||||
@ -364,21 +346,35 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
self.close_pool()
|
||||
if self.connection is None:
|
||||
return False
|
||||
return ensure_timezone(self.connection, self.ops, self.timezone_name)
|
||||
return self._configure_timezone(self.connection)
|
||||
|
||||
def _configure_timezone(self, connection):
|
||||
conn_timezone_name = connection.info.parameter_status("TimeZone")
|
||||
timezone_name = self.timezone_name
|
||||
if timezone_name and conn_timezone_name != timezone_name:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(self.ops.set_time_zone_sql(), [timezone_name])
|
||||
return True
|
||||
return False
|
||||
|
||||
def _configure_role(self, connection):
|
||||
if new_role := self.settings_dict["OPTIONS"].get("assume_role"):
|
||||
with connection.cursor() as cursor:
|
||||
sql = self.ops.compose_sql("SET ROLE %s", [new_role])
|
||||
cursor.execute(sql)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _configure_connection(self, connection):
|
||||
# This function is called from init_connection_state and from the
|
||||
# psycopg pool itself after a connection is opened. Make sure that
|
||||
# whatever is done here does not access anything on self aside from
|
||||
# variables.
|
||||
# psycopg pool itself after a connection is opened.
|
||||
|
||||
# Commit after setting the time zone.
|
||||
commit_tz = ensure_timezone(connection, self.ops, self.timezone_name)
|
||||
commit_tz = self._configure_timezone(connection)
|
||||
# Set the role on the connection. This is useful if the credential used
|
||||
# to login is not the same as the role that owns database resources. As
|
||||
# can be the case when using temporary or ephemeral credentials.
|
||||
role_name = self.settings_dict["OPTIONS"].get("assume_role")
|
||||
commit_role = ensure_role(connection, self.ops, role_name)
|
||||
commit_role = self._configure_role(connection)
|
||||
|
||||
return commit_role or commit_tz
|
||||
|
||||
|
@ -31,3 +31,7 @@ Bugfixes
|
||||
* Adjusted the deprecation warning ``stacklevel`` in
|
||||
``FieldCacheMixin.get_cache_name()`` to correctly point to the offending call
|
||||
site (:ticket:`35405`).
|
||||
|
||||
* Restored, following a regression in Django 5.1, the ability to override the
|
||||
timezone and role setting behavior used within the ``init_connection_state``
|
||||
method of the PostgreSQL backend (:ticket:`35688`).
|
||||
|
@ -567,3 +567,49 @@ class Tests(TestCase):
|
||||
)
|
||||
finally:
|
||||
new_connection.close()
|
||||
|
||||
def test_bypass_timezone_configuration(self):
|
||||
from django.db.backends.postgresql.base import DatabaseWrapper
|
||||
|
||||
class CustomDatabaseWrapper(DatabaseWrapper):
|
||||
def _configure_timezone(self, connection):
|
||||
return False
|
||||
|
||||
for Wrapper, commit in [
|
||||
(DatabaseWrapper, True),
|
||||
(CustomDatabaseWrapper, False),
|
||||
]:
|
||||
with self.subTest(wrapper=Wrapper, commit=commit):
|
||||
new_connection = no_pool_connection()
|
||||
self.addCleanup(new_connection.close)
|
||||
|
||||
# Set the database default time zone to be different from
|
||||
# the time zone in new_connection.settings_dict.
|
||||
with new_connection.cursor() as cursor:
|
||||
cursor.execute("RESET TIMEZONE")
|
||||
cursor.execute("SHOW TIMEZONE")
|
||||
db_default_tz = cursor.fetchone()[0]
|
||||
new_tz = "Europe/Paris" if db_default_tz == "UTC" else "UTC"
|
||||
new_connection.timezone_name = new_tz
|
||||
|
||||
settings = new_connection.settings_dict.copy()
|
||||
conn = new_connection.connection
|
||||
self.assertIs(Wrapper(settings)._configure_connection(conn), commit)
|
||||
|
||||
def test_bypass_role_configuration(self):
|
||||
from django.db.backends.postgresql.base import DatabaseWrapper
|
||||
|
||||
class CustomDatabaseWrapper(DatabaseWrapper):
|
||||
def _configure_role(self, connection):
|
||||
return False
|
||||
|
||||
new_connection = no_pool_connection()
|
||||
self.addCleanup(new_connection.close)
|
||||
new_connection.connect()
|
||||
|
||||
settings = new_connection.settings_dict.copy()
|
||||
settings["OPTIONS"]["assume_role"] = "django_nonexistent_role"
|
||||
conn = new_connection.connection
|
||||
self.assertIs(
|
||||
CustomDatabaseWrapper(settings)._configure_connection(conn), False
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user