mirror of https://github.com/django/django.git
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
|
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):
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
vendor = "postgresql"
|
vendor = "postgresql"
|
||||||
display_name = "PostgreSQL"
|
display_name = "PostgreSQL"
|
||||||
|
@ -364,21 +346,35 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
self.close_pool()
|
self.close_pool()
|
||||||
if self.connection is None:
|
if self.connection is None:
|
||||||
return False
|
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):
|
def _configure_connection(self, connection):
|
||||||
# This function is called from init_connection_state and from the
|
# This function is called from init_connection_state and from the
|
||||||
# psycopg pool itself after a connection is opened. Make sure that
|
# psycopg pool itself after a connection is opened.
|
||||||
# whatever is done here does not access anything on self aside from
|
|
||||||
# variables.
|
|
||||||
|
|
||||||
# Commit after setting the time zone.
|
# 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
|
# 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
|
# to login is not the same as the role that owns database resources. As
|
||||||
# can be the case when using temporary or ephemeral credentials.
|
# can be the case when using temporary or ephemeral credentials.
|
||||||
role_name = self.settings_dict["OPTIONS"].get("assume_role")
|
commit_role = self._configure_role(connection)
|
||||||
commit_role = ensure_role(connection, self.ops, role_name)
|
|
||||||
|
|
||||||
return commit_role or commit_tz
|
return commit_role or commit_tz
|
||||||
|
|
||||||
|
|
|
@ -31,3 +31,7 @@ Bugfixes
|
||||||
* Adjusted the deprecation warning ``stacklevel`` in
|
* Adjusted the deprecation warning ``stacklevel`` in
|
||||||
``FieldCacheMixin.get_cache_name()`` to correctly point to the offending call
|
``FieldCacheMixin.get_cache_name()`` to correctly point to the offending call
|
||||||
site (:ticket:`35405`).
|
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:
|
finally:
|
||||||
new_connection.close()
|
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