diff options
author | django-bot <ops@djangoproject.com> | 2022-02-03 20:24:19 +0100 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-02-07 20:37:05 +0100 |
commit | 9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch) | |
tree | f0506b668a013d0063e5fba3dbf4863b466713ba /django/db/backends/mysql/operations.py | |
parent | f68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff) | |
download | django-9c19aff7c7561e3a82978a272ecdaad40dda5c00.tar.gz |
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'django/db/backends/mysql/operations.py')
-rw-r--r-- | django/db/backends/mysql/operations.py | 281 |
1 files changed, 163 insertions, 118 deletions
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 7f1994e657..5bcc03a67b 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -15,42 +15,42 @@ class DatabaseOperations(BaseDatabaseOperations): # MySQL stores positive fields as UNSIGNED ints. integer_field_ranges = { **BaseDatabaseOperations.integer_field_ranges, - 'PositiveSmallIntegerField': (0, 65535), - 'PositiveIntegerField': (0, 4294967295), - 'PositiveBigIntegerField': (0, 18446744073709551615), + "PositiveSmallIntegerField": (0, 65535), + "PositiveIntegerField": (0, 4294967295), + "PositiveBigIntegerField": (0, 18446744073709551615), } cast_data_types = { - 'AutoField': 'signed integer', - 'BigAutoField': 'signed integer', - 'SmallAutoField': 'signed integer', - 'CharField': 'char(%(max_length)s)', - 'DecimalField': 'decimal(%(max_digits)s, %(decimal_places)s)', - 'TextField': 'char', - 'IntegerField': 'signed integer', - 'BigIntegerField': 'signed integer', - 'SmallIntegerField': 'signed integer', - 'PositiveBigIntegerField': 'unsigned integer', - 'PositiveIntegerField': 'unsigned integer', - 'PositiveSmallIntegerField': 'unsigned integer', - 'DurationField': 'signed integer', + "AutoField": "signed integer", + "BigAutoField": "signed integer", + "SmallAutoField": "signed integer", + "CharField": "char(%(max_length)s)", + "DecimalField": "decimal(%(max_digits)s, %(decimal_places)s)", + "TextField": "char", + "IntegerField": "signed integer", + "BigIntegerField": "signed integer", + "SmallIntegerField": "signed integer", + "PositiveBigIntegerField": "unsigned integer", + "PositiveIntegerField": "unsigned integer", + "PositiveSmallIntegerField": "unsigned integer", + "DurationField": "signed integer", } - cast_char_field_without_max_length = 'char' - explain_prefix = 'EXPLAIN' + cast_char_field_without_max_length = "char" + explain_prefix = "EXPLAIN" def date_extract_sql(self, lookup_type, field_name): # https://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - if lookup_type == 'week_day': + if lookup_type == "week_day": # DAYOFWEEK() returns an integer, 1-7, Sunday=1. return "DAYOFWEEK(%s)" % field_name - elif lookup_type == 'iso_week_day': + elif lookup_type == "iso_week_day": # WEEKDAY() returns an integer, 0-6, Monday=0. return "WEEKDAY(%s) + 1" % field_name - elif lookup_type == 'week': + elif lookup_type == "week": # Override the value of default_week_format for consistency with # other database backends. # Mode 3: Monday, 1-53, with 4 or more days this year. return "WEEK(%s, 3)" % field_name - elif lookup_type == 'iso_year': + elif lookup_type == "iso_year": # Get the year part from the YEARWEEK function, which returns a # number as year * 100 + week. return "TRUNCATE(YEARWEEK(%s, 3), -2) / 100" % field_name @@ -61,26 +61,25 @@ class DatabaseOperations(BaseDatabaseOperations): def date_trunc_sql(self, lookup_type, field_name, tzname=None): field_name = self._convert_field_to_tz(field_name, tzname) fields = { - 'year': '%%Y-01-01', - 'month': '%%Y-%%m-01', + "year": "%%Y-01-01", + "month": "%%Y-%%m-01", } # Use double percents to escape. if lookup_type in fields: format_str = fields[lookup_type] return "CAST(DATE_FORMAT(%s, '%s') AS DATE)" % (field_name, format_str) - elif lookup_type == 'quarter': - return "MAKEDATE(YEAR(%s), 1) + INTERVAL QUARTER(%s) QUARTER - INTERVAL 1 QUARTER" % ( - field_name, field_name - ) - elif lookup_type == 'week': - return "DATE_SUB(%s, INTERVAL WEEKDAY(%s) DAY)" % ( - field_name, field_name + elif lookup_type == "quarter": + return ( + "MAKEDATE(YEAR(%s), 1) + INTERVAL QUARTER(%s) QUARTER - INTERVAL 1 QUARTER" + % (field_name, field_name) ) + elif lookup_type == "week": + return "DATE_SUB(%s, INTERVAL WEEKDAY(%s) DAY)" % (field_name, field_name) else: return "DATE(%s)" % (field_name) def _prepare_tzname_delta(self, tzname): tzname, sign, offset = split_tzname_delta(tzname) - return f'{sign}{offset}' if offset else tzname + return f"{sign}{offset}" if offset else tzname def _convert_field_to_tz(self, field_name, tzname): if tzname and settings.USE_TZ and self.connection.timezone_name != tzname: @@ -105,16 +104,23 @@ class DatabaseOperations(BaseDatabaseOperations): def datetime_trunc_sql(self, lookup_type, 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') - if lookup_type == 'quarter': + 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") + if lookup_type == "quarter": return ( "CAST(DATE_FORMAT(MAKEDATE(YEAR({field_name}), 1) + " - "INTERVAL QUARTER({field_name}) QUARTER - " + - "INTERVAL 1 QUARTER, '%%Y-%%m-01 00:00:00') AS DATETIME)" + "INTERVAL QUARTER({field_name}) QUARTER - " + + "INTERVAL 1 QUARTER, '%%Y-%%m-01 00:00:00') AS DATETIME)" ).format(field_name=field_name) - if lookup_type == 'week': + if lookup_type == "week": return ( "CAST(DATE_FORMAT(DATE_SUB({field_name}, " "INTERVAL WEEKDAY({field_name}) DAY), " @@ -125,16 +131,16 @@ class DatabaseOperations(BaseDatabaseOperations): except ValueError: sql = field_name else: - format_str = ''.join(format[:i] + format_def[i:]) + format_str = "".join(format[:i] + format_def[i:]) sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) return sql def time_trunc_sql(self, lookup_type, field_name, tzname=None): field_name = self._convert_field_to_tz(field_name, tzname) fields = { - 'hour': '%%H:00:00', - 'minute': '%%H:%%i:00', - 'second': '%%H:%%i:%%s', + "hour": "%%H:00:00", + "minute": "%%H:%%i:00", + "second": "%%H:%%i:%%s", } # Use double percents to escape. if lookup_type in fields: format_str = fields[lookup_type] @@ -150,7 +156,7 @@ class DatabaseOperations(BaseDatabaseOperations): return cursor.fetchall() def format_for_duration_arithmetic(self, sql): - return 'INTERVAL %s MICROSECOND' % sql + return "INTERVAL %s MICROSECOND" % sql def force_no_ordering(self): """ @@ -168,7 +174,7 @@ class DatabaseOperations(BaseDatabaseOperations): # attribute where the exact query sent to the database is saved. # See MySQLdb/cursors.py in the source distribution. # MySQLdb returns string, PyMySQL bytes. - return force_str(getattr(cursor, '_executed', None), errors='replace') + return force_str(getattr(cursor, "_executed", None), errors="replace") def no_limit_value(self): # 2**64 - 1, as recommended by the MySQL documentation @@ -183,50 +189,58 @@ class DatabaseOperations(BaseDatabaseOperations): # MySQL and MariaDB < 10.5.0 don't support an INSERT...RETURNING # statement. if not fields: - return '', () + return "", () columns = [ - '%s.%s' % ( + "%s.%s" + % ( self.quote_name(field.model._meta.db_table), self.quote_name(field.column), - ) for field in fields + ) + for field in fields ] - return 'RETURNING %s' % ', '.join(columns), () + return "RETURNING %s" % ", ".join(columns), () def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False): if not tables: return [] - sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + sql = ["SET FOREIGN_KEY_CHECKS = 0;"] if reset_sequences: # It's faster to TRUNCATE tables that require a sequence reset # since ALTER TABLE AUTO_INCREMENT is slower than TRUNCATE. sql.extend( - '%s %s;' % ( - style.SQL_KEYWORD('TRUNCATE'), + "%s %s;" + % ( + style.SQL_KEYWORD("TRUNCATE"), style.SQL_FIELD(self.quote_name(table_name)), - ) for table_name in tables + ) + for table_name in tables ) else: # Otherwise issue a simple DELETE since it's faster than TRUNCATE # and preserves sequences. sql.extend( - '%s %s %s;' % ( - style.SQL_KEYWORD('DELETE'), - style.SQL_KEYWORD('FROM'), + "%s %s %s;" + % ( + style.SQL_KEYWORD("DELETE"), + style.SQL_KEYWORD("FROM"), style.SQL_FIELD(self.quote_name(table_name)), - ) for table_name in tables + ) + for table_name in tables ) - sql.append('SET FOREIGN_KEY_CHECKS = 1;') + sql.append("SET FOREIGN_KEY_CHECKS = 1;") return sql def sequence_reset_by_name_sql(self, style, sequences): return [ - '%s %s %s %s = 1;' % ( - style.SQL_KEYWORD('ALTER'), - style.SQL_KEYWORD('TABLE'), - style.SQL_FIELD(self.quote_name(sequence_info['table'])), - style.SQL_FIELD('AUTO_INCREMENT'), - ) for sequence_info in sequences + "%s %s %s %s = 1;" + % ( + style.SQL_KEYWORD("ALTER"), + style.SQL_KEYWORD("TABLE"), + style.SQL_FIELD(self.quote_name(sequence_info["table"])), + style.SQL_FIELD("AUTO_INCREMENT"), + ) + for sequence_info in sequences ] def validate_autopk_value(self, value): @@ -234,7 +248,7 @@ class DatabaseOperations(BaseDatabaseOperations): # NO_AUTO_VALUE_ON_ZERO SQL mode. if value == 0 and not self.connection.features.allows_auto_pk_0: raise ValueError( - 'The database backend does not accept 0 as a value for AutoField.' + "The database backend does not accept 0 as a value for AutoField." ) return value @@ -243,7 +257,7 @@ class DatabaseOperations(BaseDatabaseOperations): return None # Expression values are adapted by the database. - if hasattr(value, 'resolve_expression'): + if hasattr(value, "resolve_expression"): return value # MySQL doesn't support tz-aware datetimes @@ -251,7 +265,9 @@ class DatabaseOperations(BaseDatabaseOperations): if settings.USE_TZ: value = timezone.make_naive(value, self.connection.timezone) else: - raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.") + raise ValueError( + "MySQL backend does not support timezone-aware datetimes when USE_TZ is False." + ) return str(value) def adapt_timefield_value(self, value): @@ -259,20 +275,20 @@ class DatabaseOperations(BaseDatabaseOperations): return None # Expression values are adapted by the database. - if hasattr(value, 'resolve_expression'): + if hasattr(value, "resolve_expression"): return value # MySQL doesn't support tz-aware times if timezone.is_aware(value): raise ValueError("MySQL backend does not support timezone-aware times.") - return value.isoformat(timespec='microseconds') + return value.isoformat(timespec="microseconds") def max_name_length(self): return 64 def pk_default_value(self): - return 'NULL' + return "NULL" def bulk_insert_sql(self, fields, placeholder_rows): placeholder_rows_sql = (", ".join(row) for row in placeholder_rows) @@ -280,27 +296,27 @@ class DatabaseOperations(BaseDatabaseOperations): return "VALUES " + values_sql def combine_expression(self, connector, sub_expressions): - if connector == '^': - return 'POW(%s)' % ','.join(sub_expressions) + if connector == "^": + return "POW(%s)" % ",".join(sub_expressions) # Convert the result to a signed integer since MySQL's binary operators # return an unsigned integer. - elif connector in ('&', '|', '<<', '#'): - connector = '^' if connector == '#' else connector - return 'CONVERT(%s, SIGNED)' % connector.join(sub_expressions) - elif connector == '>>': + elif connector in ("&", "|", "<<", "#"): + connector = "^" if connector == "#" else connector + return "CONVERT(%s, SIGNED)" % connector.join(sub_expressions) + elif connector == ">>": lhs, rhs = sub_expressions - return 'FLOOR(%(lhs)s / POW(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs} + return "FLOOR(%(lhs)s / POW(2, %(rhs)s))" % {"lhs": lhs, "rhs": rhs} return super().combine_expression(connector, sub_expressions) def get_db_converters(self, expression): converters = super().get_db_converters(expression) internal_type = expression.output_field.get_internal_type() - if internal_type == 'BooleanField': + if internal_type == "BooleanField": converters.append(self.convert_booleanfield_value) - elif internal_type == 'DateTimeField': + elif internal_type == "DateTimeField": if settings.USE_TZ: converters.append(self.convert_datetimefield_value) - elif internal_type == 'UUIDField': + elif internal_type == "UUIDField": converters.append(self.convert_uuidfield_value) return converters @@ -320,66 +336,88 @@ class DatabaseOperations(BaseDatabaseOperations): return value def binary_placeholder_sql(self, value): - return '_binary %s' if value is not None and not hasattr(value, 'as_sql') else '%s' + return ( + "_binary %s" if value is not None and not hasattr(value, "as_sql") else "%s" + ) def subtract_temporals(self, internal_type, lhs, rhs): lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs - if internal_type == 'TimeField': + if internal_type == "TimeField": if self.connection.mysql_is_mariadb: # MariaDB includes the microsecond component in TIME_TO_SEC as # a decimal. MySQL returns an integer without microseconds. - return 'CAST((TIME_TO_SEC(%(lhs)s) - TIME_TO_SEC(%(rhs)s)) * 1000000 AS SIGNED)' % { - 'lhs': lhs_sql, 'rhs': rhs_sql - }, (*lhs_params, *rhs_params) + return "CAST((TIME_TO_SEC(%(lhs)s) - TIME_TO_SEC(%(rhs)s)) * 1000000 AS SIGNED)" % { + "lhs": lhs_sql, + "rhs": rhs_sql, + }, ( + *lhs_params, + *rhs_params, + ) return ( "((TIME_TO_SEC(%(lhs)s) * 1000000 + MICROSECOND(%(lhs)s)) -" " (TIME_TO_SEC(%(rhs)s) * 1000000 + MICROSECOND(%(rhs)s)))" - ) % {'lhs': lhs_sql, 'rhs': rhs_sql}, tuple(lhs_params) * 2 + tuple(rhs_params) * 2 + ) % {"lhs": lhs_sql, "rhs": rhs_sql}, tuple(lhs_params) * 2 + tuple( + rhs_params + ) * 2 params = (*rhs_params, *lhs_params) return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), params def explain_query_prefix(self, format=None, **options): # Alias MySQL's TRADITIONAL to TEXT for consistency with other backends. - if format and format.upper() == 'TEXT': - format = 'TRADITIONAL' - elif not format and 'TREE' in self.connection.features.supported_explain_formats: + if format and format.upper() == "TEXT": + format = "TRADITIONAL" + elif ( + not format and "TREE" in self.connection.features.supported_explain_formats + ): # Use TREE by default (if supported) as it's more informative. - format = 'TREE' - analyze = options.pop('analyze', False) + format = "TREE" + analyze = options.pop("analyze", False) prefix = super().explain_query_prefix(format, **options) if analyze and self.connection.features.supports_explain_analyze: # MariaDB uses ANALYZE instead of EXPLAIN ANALYZE. - prefix = 'ANALYZE' if self.connection.mysql_is_mariadb else prefix + ' ANALYZE' + prefix = ( + "ANALYZE" if self.connection.mysql_is_mariadb else prefix + " ANALYZE" + ) if format and not (analyze and not self.connection.mysql_is_mariadb): # Only MariaDB supports the analyze option with formats. - prefix += ' FORMAT=%s' % format + prefix += " FORMAT=%s" % format return prefix def regex_lookup(self, lookup_type): # REGEXP BINARY doesn't work correctly in MySQL 8+ and REGEXP_LIKE # doesn't exist in MySQL 5.x or in MariaDB. - if self.connection.mysql_version < (8, 0, 0) or self.connection.mysql_is_mariadb: - if lookup_type == 'regex': - return '%s REGEXP BINARY %s' - return '%s REGEXP %s' - - match_option = 'c' if lookup_type == 'regex' else 'i' + if ( + self.connection.mysql_version < (8, 0, 0) + or self.connection.mysql_is_mariadb + ): + if lookup_type == "regex": + return "%s REGEXP BINARY %s" + return "%s REGEXP %s" + + match_option = "c" if lookup_type == "regex" else "i" return "REGEXP_LIKE(%%s, %%s, '%s')" % match_option def insert_statement(self, on_conflict=None): if on_conflict == OnConflict.IGNORE: - return 'INSERT IGNORE INTO' + return "INSERT IGNORE INTO" return super().insert_statement(on_conflict=on_conflict) def lookup_cast(self, lookup_type, internal_type=None): - lookup = '%s' - if internal_type == 'JSONField': + lookup = "%s" + if internal_type == "JSONField": if self.connection.mysql_is_mariadb or lookup_type in ( - 'iexact', 'contains', 'icontains', 'startswith', 'istartswith', - 'endswith', 'iendswith', 'regex', 'iregex', + "iexact", + "contains", + "icontains", + "startswith", + "istartswith", + "endswith", + "iendswith", + "regex", + "iregex", ): - lookup = 'JSON_UNQUOTE(%s)' + lookup = "JSON_UNQUOTE(%s)" return lookup def conditional_expression_supported_in_where_clause(self, expression): @@ -388,31 +426,38 @@ class DatabaseOperations(BaseDatabaseOperations): if isinstance(expression, (Exists, Lookup)): return True if isinstance(expression, ExpressionWrapper) and expression.conditional: - return self.conditional_expression_supported_in_where_clause(expression.expression) - if getattr(expression, 'conditional', False): + return self.conditional_expression_supported_in_where_clause( + expression.expression + ) + if getattr(expression, "conditional", False): return False return super().conditional_expression_supported_in_where_clause(expression) def on_conflict_suffix_sql(self, fields, on_conflict, update_fields, unique_fields): if on_conflict == OnConflict.UPDATE: - conflict_suffix_sql = 'ON DUPLICATE KEY UPDATE %(fields)s' - field_sql = '%(field)s = VALUES(%(field)s)' + conflict_suffix_sql = "ON DUPLICATE KEY UPDATE %(fields)s" + field_sql = "%(field)s = VALUES(%(field)s)" # The use of VALUES() is deprecated in MySQL 8.0.20+. Instead, use # aliases for the new row and its columns available in MySQL # 8.0.19+. if not self.connection.mysql_is_mariadb: if self.connection.mysql_version >= (8, 0, 19): - conflict_suffix_sql = f'AS new {conflict_suffix_sql}' - field_sql = '%(field)s = new.%(field)s' + conflict_suffix_sql = f"AS new {conflict_suffix_sql}" + field_sql = "%(field)s = new.%(field)s" # VALUES() was renamed to VALUE() in MariaDB 10.3.3+. elif self.connection.mysql_version >= (10, 3, 3): - field_sql = '%(field)s = VALUE(%(field)s)' + field_sql = "%(field)s = VALUE(%(field)s)" - fields = ', '.join([ - field_sql % {'field': field} - for field in map(self.quote_name, update_fields) - ]) - return conflict_suffix_sql % {'fields': fields} + fields = ", ".join( + [ + field_sql % {"field": field} + for field in map(self.quote_name, update_fields) + ] + ) + return conflict_suffix_sql % {"fields": fields} return super().on_conflict_suffix_sql( - fields, on_conflict, update_fields, unique_fields, + fields, + on_conflict, + update_fields, + unique_fields, ) |