Skip to content

Commit

Permalink
Fixed #35688 -- Restored timezone and role setters to be PostgreSQL D…
Browse files Browse the repository at this point in the history
…atabaseWrapper 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 fad334e.

Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
  • Loading branch information
sarahboyce and nessita committed Aug 28, 2024
1 parent 26a6794 commit 7380ac5
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 25 deletions.
46 changes: 21 additions & 25 deletions django/db/backends/postgresql/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -364,21 +346,35 @@ def ensure_timezone(self):
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

Expand Down
4 changes: 4 additions & 0 deletions docs/releases/5.1.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
46 changes: 46 additions & 0 deletions tests/backends/postgresql/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,49 @@ def test_compose_sql_when_no_connection(self):
)
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
)

0 comments on commit 7380ac5

Please sign in to comment.