From 15c14f6f16af22b970e5693b349113d80f10f82d Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Wed, 1 Feb 2017 14:48:04 +0100 Subject: [PATCH] Fixed #27802 -- Unified return value of db backend datetime SQL methods. --- django/db/backends/mysql/operations.py | 26 ++++++++------------ django/db/backends/oracle/operations.py | 11 +++------ django/db/backends/postgresql/operations.py | 27 ++++++++------------- django/db/backends/sqlite3/operations.py | 21 +++++++++++----- django/db/models/functions/datetime.py | 15 ++++-------- docs/releases/2.0.txt | 5 +++- 6 files changed, 48 insertions(+), 57 deletions(-) diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 881ada60c7..b47136df26 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -44,29 +44,23 @@ class DatabaseOperations(BaseDatabaseOperations): def _convert_field_to_tz(self, field_name, tzname): if settings.USE_TZ: - field_name = "CONVERT_TZ(%s, 'UTC', %%s)" % field_name - params = [tzname] - else: - params = [] - return field_name, params + field_name = "CONVERT_TZ(%s, 'UTC', '%s')" % (field_name, tzname) + return field_name def datetime_cast_date_sql(self, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) - sql = "DATE(%s)" % field_name - return sql, params + field_name = self._convert_field_to_tz(field_name, tzname) + return "DATE(%s)" % field_name def datetime_cast_time_sql(self, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) - sql = "TIME(%s)" % field_name - return sql, params + field_name = self._convert_field_to_tz(field_name, tzname) + return "TIME(%s)" % field_name def datetime_extract_sql(self, lookup_type, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) - sql = self.date_extract_sql(lookup_type, field_name) - return sql, params + field_name = self._convert_field_to_tz(field_name, tzname) + return self.date_extract_sql(lookup_type, field_name) def datetime_trunc_sql(self, lookup_type, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) + field_name = self._convert_field_to_tz(field_name, tzname) fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape. format_def = ('0000-', '01', '-01', ' 00:', '00', ':00') @@ -77,7 +71,7 @@ class DatabaseOperations(BaseDatabaseOperations): else: format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]]) sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) - return sql, params + return sql def time_trunc_sql(self, lookup_type, field_name): fields = { diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py index f8fee3bd3d..7cc33af537 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -122,19 +122,16 @@ WHEN (new.%(col_name)s IS NULL) def datetime_cast_date_sql(self, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) - sql = 'TRUNC(%s)' % field_name - return sql, [] + return 'TRUNC(%s)' % field_name def datetime_cast_time_sql(self, field_name, tzname): # Since `TimeField` values are stored as TIMESTAMP where only the date # part is ignored, convert the field to the specified timezone. - field_name = self._convert_field_to_tz(field_name, tzname) - return field_name, [] + return self._convert_field_to_tz(field_name, tzname) def datetime_extract_sql(self, lookup_type, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) - sql = self.date_extract_sql(lookup_type, field_name) - return sql, [] + return self.date_extract_sql(lookup_type, field_name) def datetime_trunc_sql(self, lookup_type, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) @@ -149,7 +146,7 @@ WHEN (new.%(col_name)s IS NULL) sql = "TRUNC(%s, 'MI')" % field_name else: sql = "CAST(%s AS DATE)" % field_name # Cast to DATE removes sub-second precision. - return sql, [] + return sql def time_trunc_sql(self, lookup_type, field_name): # The implementation is similar to `datetime_trunc_sql` as both diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py index e612a60ca8..fde02cbb78 100644 --- a/django/db/backends/postgresql/operations.py +++ b/django/db/backends/postgresql/operations.py @@ -32,32 +32,25 @@ class DatabaseOperations(BaseDatabaseOperations): def _convert_field_to_tz(self, field_name, tzname): if settings.USE_TZ: - field_name = "%s AT TIME ZONE %%s" % field_name - params = [tzname] - else: - params = [] - return field_name, params + field_name = "%s AT TIME ZONE '%s'" % (field_name, tzname) + return field_name def datetime_cast_date_sql(self, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) - sql = '(%s)::date' % field_name - return sql, params + field_name = self._convert_field_to_tz(field_name, tzname) + return '(%s)::date' % field_name def datetime_cast_time_sql(self, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) - sql = '(%s)::time' % field_name - return sql, params + field_name = self._convert_field_to_tz(field_name, tzname) + return '(%s)::time' % field_name def datetime_extract_sql(self, lookup_type, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) - sql = self.date_extract_sql(lookup_type, field_name) - return sql, params + field_name = self._convert_field_to_tz(field_name, tzname) + return self.date_extract_sql(lookup_type, field_name) def datetime_trunc_sql(self, lookup_type, field_name, tzname): - field_name, params = self._convert_field_to_tz(field_name, tzname) + field_name = self._convert_field_to_tz(field_name, tzname) # https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC - sql = "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) - return sql, params + return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) def time_trunc_sql(self, lookup_type, field_name): return "DATE_TRUNC('%s', %s)::time" % (lookup_type, field_name) diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py index 4f66b66de9..dd9669d02d 100644 --- a/django/db/backends/sqlite3/operations.py +++ b/django/db/backends/sqlite3/operations.py @@ -70,21 +70,30 @@ class DatabaseOperations(BaseDatabaseOperations): # cause a collision with a field name). return "django_time_trunc('%s', %s)" % (lookup_type.lower(), field_name) + def _convert_tzname_to_sql(self, tzname): + return "'%s'" % tzname if settings.USE_TZ else 'NULL' + def datetime_cast_date_sql(self, field_name, tzname): - return "django_datetime_cast_date(%s, %%s)" % field_name, [tzname] + return "django_datetime_cast_date(%s, %s)" % ( + field_name, self._convert_tzname_to_sql(tzname), + ) def datetime_cast_time_sql(self, field_name, tzname): - return "django_datetime_cast_time(%s, %%s)" % field_name, [tzname] + return "django_datetime_cast_time(%s, %s)" % ( + field_name, self._convert_tzname_to_sql(tzname), + ) def datetime_extract_sql(self, lookup_type, field_name, tzname): # Same comment as in date_extract_sql. - return "django_datetime_extract('%s', %s, %%s)" % ( - lookup_type.lower(), field_name), [tzname] + return "django_datetime_extract('%s', %s, %s)" % ( + lookup_type.lower(), field_name, self._convert_tzname_to_sql(tzname), + ) def datetime_trunc_sql(self, lookup_type, field_name, tzname): # Same comment as in date_trunc_sql. - return "django_datetime_trunc('%s', %s, %%s)" % ( - lookup_type.lower(), field_name), [tzname] + return "django_datetime_trunc('%s', %s, %s)" % ( + lookup_type.lower(), field_name, self._convert_tzname_to_sql(tzname), + ) def time_extract_sql(self, lookup_type, field_name): # sqlite doesn't support extract, so we fake it with the user-defined diff --git a/django/db/models/functions/datetime.py b/django/db/models/functions/datetime.py index 19afc271b5..debf65db3a 100644 --- a/django/db/models/functions/datetime.py +++ b/django/db/models/functions/datetime.py @@ -44,8 +44,7 @@ class Extract(TimezoneMixin, Transform): lhs_output_field = self.lhs.output_field if isinstance(lhs_output_field, DateTimeField): tzname = self.get_tzname() - sql, tz_params = connection.ops.datetime_extract_sql(self.lookup_name, sql, tzname) - params.extend(tz_params) + sql = connection.ops.datetime_extract_sql(self.lookup_name, sql, tzname) elif isinstance(lhs_output_field, DateField): sql = connection.ops.date_extract_sql(self.lookup_name, sql) elif isinstance(lhs_output_field, TimeField): @@ -150,16 +149,14 @@ class TruncBase(TimezoneMixin, Transform): inner_sql = inner_sql.replace('%s', '%%s') if isinstance(self.output_field, DateTimeField): tzname = self.get_tzname() - sql, params = connection.ops.datetime_trunc_sql(self.kind, inner_sql, tzname) + sql = connection.ops.datetime_trunc_sql(self.kind, inner_sql, tzname) elif isinstance(self.output_field, DateField): sql = connection.ops.date_trunc_sql(self.kind, inner_sql) - params = [] elif isinstance(self.output_field, TimeField): sql = connection.ops.time_trunc_sql(self.kind, inner_sql) - params = [] else: raise ValueError('Trunc only valid on DateField, TimeField, or DateTimeField.') - return sql, inner_params + params + return sql, inner_params def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) @@ -237,8 +234,7 @@ class TruncDate(TruncBase): # Cast to date rather than truncate to date. lhs, lhs_params = compiler.compile(self.lhs) tzname = timezone.get_current_timezone_name() if settings.USE_TZ else None - sql, tz_params = connection.ops.datetime_cast_date_sql(lhs, tzname) - lhs_params.extend(tz_params) + sql = connection.ops.datetime_cast_date_sql(lhs, tzname) return sql, lhs_params @@ -254,8 +250,7 @@ class TruncTime(TruncBase): # Cast to date rather than truncate to date. lhs, lhs_params = compiler.compile(self.lhs) tzname = timezone.get_current_timezone_name() if settings.USE_TZ else None - sql, tz_params = connection.ops.datetime_cast_time_sql(lhs, tzname) - lhs_params.extend(tz_params) + sql = connection.ops.datetime_cast_time_sql(lhs, tzname) return sql, lhs_params diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index f5e5cd4d7c..8afafb200d 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -220,7 +220,10 @@ bytestrings in certain code paths. Database backend API -------------------- -* ... +* The ``DatabaseOperations.datetime_cast_date_sql()``, + ``datetime_cast_time_sql()``, ``datetime_trunc_sql()``, and + ``datetime_extract_sql()`` methods now return only the SQL to perform the + operation instead of SQL and a list of parameters. Dropped support for Oracle 11.2 -------------------------------