summaryrefslogtreecommitdiff
path: root/django/db/backends/mysql/operations.py
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2015-01-12 15:20:40 -0500
committerTim Graham <timograham@gmail.com>2015-01-14 14:16:20 -0500
commit28308078f397d1de36fd0da417ac7da2544ba12d (patch)
tree80207ff582b2350d058c1c7c49072b761391c04f /django/db/backends/mysql/operations.py
parent737d24923ac69bb8b89af1bb2f3f4c4c744349e8 (diff)
downloaddjango-28308078f397d1de36fd0da417ac7da2544ba12d.tar.gz
Fixed #22603 -- Reorganized classes in django.db.backends.
Diffstat (limited to 'django/db/backends/mysql/operations.py')
-rw-r--r--django/db/backends/mysql/operations.py200
1 files changed, 200 insertions, 0 deletions
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py
new file mode 100644
index 0000000000..7edc13b3ff
--- /dev/null
+++ b/django/db/backends/mysql/operations.py
@@ -0,0 +1,200 @@
+from __future__ import unicode_literals
+
+import uuid
+
+from django.conf import settings
+from django.db.backends.base.operations import BaseDatabaseOperations
+from django.utils import six, timezone
+from django.utils.encoding import force_text
+
+
+class DatabaseOperations(BaseDatabaseOperations):
+ compiler_module = "django.db.backends.mysql.compiler"
+
+ # MySQL stores positive fields as UNSIGNED ints.
+ integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges,
+ PositiveSmallIntegerField=(0, 4294967295),
+ PositiveIntegerField=(0, 18446744073709551615),
+ )
+
+ def date_extract_sql(self, lookup_type, field_name):
+ # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
+ if lookup_type == 'week_day':
+ # DAYOFWEEK() returns an integer, 1-7, Sunday=1.
+ # Note: WEEKDAY() returns 0-6, Monday=0.
+ return "DAYOFWEEK(%s)" % field_name
+ else:
+ return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
+
+ def date_trunc_sql(self, lookup_type, field_name):
+ 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')
+ try:
+ i = fields.index(lookup_type) + 1
+ except ValueError:
+ sql = field_name
+ 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
+
+ def datetime_extract_sql(self, lookup_type, field_name, tzname):
+ if settings.USE_TZ:
+ field_name = "CONVERT_TZ(%s, 'UTC', %%s)" % field_name
+ params = [tzname]
+ else:
+ params = []
+ # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
+ if lookup_type == 'week_day':
+ # DAYOFWEEK() returns an integer, 1-7, Sunday=1.
+ # Note: WEEKDAY() returns 0-6, Monday=0.
+ sql = "DAYOFWEEK(%s)" % field_name
+ else:
+ sql = "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
+ return sql, params
+
+ def datetime_trunc_sql(self, lookup_type, field_name, tzname):
+ if settings.USE_TZ:
+ field_name = "CONVERT_TZ(%s, 'UTC', %%s)" % field_name
+ params = [tzname]
+ else:
+ params = []
+ 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')
+ try:
+ i = fields.index(lookup_type) + 1
+ except ValueError:
+ sql = field_name
+ 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
+
+ def date_interval_sql(self, timedelta):
+ return "INTERVAL '%d 0:0:%d:%d' DAY_MICROSECOND" % (
+ timedelta.days, timedelta.seconds, timedelta.microseconds), []
+
+ def format_for_duration_arithmetic(self, sql):
+ return 'INTERVAL %s MICROSECOND' % sql
+
+ def drop_foreignkey_sql(self):
+ return "DROP FOREIGN KEY"
+
+ def force_no_ordering(self):
+ """
+ "ORDER BY NULL" prevents MySQL from implicitly ordering by grouped
+ columns. If no ordering would otherwise be applied, we don't want any
+ implicit sorting going on.
+ """
+ return [(None, ("NULL", [], False))]
+
+ def fulltext_search_sql(self, field_name):
+ return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
+
+ def last_executed_query(self, cursor, sql, params):
+ # With MySQLdb, cursor objects have an (undocumented) "_last_executed"
+ # attribute where the exact query sent to the database is saved.
+ # See MySQLdb/cursors.py in the source distribution.
+ return force_text(getattr(cursor, '_last_executed', None), errors='replace')
+
+ def no_limit_value(self):
+ # 2**64 - 1, as recommended by the MySQL documentation
+ return 18446744073709551615
+
+ def quote_name(self, name):
+ if name.startswith("`") and name.endswith("`"):
+ return name # Quoting once is enough.
+ return "`%s`" % name
+
+ def random_function_sql(self):
+ return 'RAND()'
+
+ def sql_flush(self, style, tables, sequences, allow_cascade=False):
+ # NB: The generated SQL below is specific to MySQL
+ # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
+ # to clear all tables of all data
+ if tables:
+ sql = ['SET FOREIGN_KEY_CHECKS = 0;']
+ for table in tables:
+ sql.append('%s %s;' % (
+ style.SQL_KEYWORD('TRUNCATE'),
+ style.SQL_FIELD(self.quote_name(table)),
+ ))
+ sql.append('SET FOREIGN_KEY_CHECKS = 1;')
+ sql.extend(self.sequence_reset_by_name_sql(style, sequences))
+ return sql
+ else:
+ return []
+
+ def validate_autopk_value(self, value):
+ # MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
+ if value == 0:
+ raise ValueError('The database backend does not accept 0 as a '
+ 'value for AutoField.')
+ return value
+
+ def value_to_db_datetime(self, value):
+ if value is None:
+ return None
+
+ # MySQL doesn't support tz-aware datetimes
+ if timezone.is_aware(value):
+ if settings.USE_TZ:
+ value = value.astimezone(timezone.utc).replace(tzinfo=None)
+ else:
+ raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.")
+
+ return six.text_type(value)
+
+ def value_to_db_time(self, value):
+ if value is None:
+ return None
+
+ # MySQL doesn't support tz-aware times
+ if timezone.is_aware(value):
+ raise ValueError("MySQL backend does not support timezone-aware times.")
+
+ return six.text_type(value)
+
+ def max_name_length(self):
+ return 64
+
+ def bulk_insert_sql(self, fields, num_values):
+ items_sql = "(%s)" % ", ".join(["%s"] * len(fields))
+ return "VALUES " + ", ".join([items_sql] * num_values)
+
+ def combine_expression(self, connector, sub_expressions):
+ """
+ MySQL requires special cases for ^ operators in query expressions
+ """
+ if connector == '^':
+ return 'POW(%s)' % ','.join(sub_expressions)
+ return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
+
+ def get_db_converters(self, expression):
+ converters = super(DatabaseOperations, self).get_db_converters(expression)
+ internal_type = expression.output_field.get_internal_type()
+ if internal_type in ['BooleanField', 'NullBooleanField']:
+ converters.append(self.convert_booleanfield_value)
+ if internal_type == 'UUIDField':
+ converters.append(self.convert_uuidfield_value)
+ if internal_type == 'TextField':
+ converters.append(self.convert_textfield_value)
+ return converters
+
+ def convert_booleanfield_value(self, value, expression, context):
+ if value in (0, 1):
+ value = bool(value)
+ return value
+
+ def convert_uuidfield_value(self, value, expression, context):
+ if value is not None:
+ value = uuid.UUID(value)
+ return value
+
+ def convert_textfield_value(self, value, expression, context):
+ if value is not None:
+ value = force_text(value)
+ return value