You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

215 lines
8.4 KiB

  1. from __future__ import unicode_literals
  2. import uuid
  3. from django.conf import settings
  4. from django.db.backends.base.operations import BaseDatabaseOperations
  5. from django.utils import six, timezone
  6. from django.utils.encoding import force_text
  7. class DatabaseOperations(BaseDatabaseOperations):
  8. compiler_module = "django.db.backends.mysql.compiler"
  9. # MySQL stores positive fields as UNSIGNED ints.
  10. integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges,
  11. PositiveSmallIntegerField=(0, 65535),
  12. PositiveIntegerField=(0, 4294967295),
  13. )
  14. def date_extract_sql(self, lookup_type, field_name):
  15. # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
  16. if lookup_type == 'week_day':
  17. # DAYOFWEEK() returns an integer, 1-7, Sunday=1.
  18. # Note: WEEKDAY() returns 0-6, Monday=0.
  19. return "DAYOFWEEK(%s)" % field_name
  20. else:
  21. return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
  22. def date_trunc_sql(self, lookup_type, field_name):
  23. fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
  24. format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
  25. format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
  26. try:
  27. i = fields.index(lookup_type) + 1
  28. except ValueError:
  29. sql = field_name
  30. else:
  31. format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
  32. sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
  33. return sql
  34. def _convert_field_to_tz(self, field_name, tzname):
  35. if settings.USE_TZ:
  36. field_name = "CONVERT_TZ(%s, 'UTC', %%s)" % field_name
  37. params = [tzname]
  38. else:
  39. params = []
  40. return field_name, params
  41. def datetime_cast_date_sql(self, field_name, tzname):
  42. field_name, params = self._convert_field_to_tz(field_name, tzname)
  43. sql = "DATE(%s)" % field_name
  44. return sql, params
  45. def datetime_extract_sql(self, lookup_type, field_name, tzname):
  46. field_name, params = self._convert_field_to_tz(field_name, tzname)
  47. sql = self.date_extract_sql(lookup_type, field_name)
  48. return sql, params
  49. def datetime_trunc_sql(self, lookup_type, field_name, tzname):
  50. field_name, params = self._convert_field_to_tz(field_name, tzname)
  51. fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
  52. format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
  53. format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
  54. try:
  55. i = fields.index(lookup_type) + 1
  56. except ValueError:
  57. sql = field_name
  58. else:
  59. format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
  60. sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
  61. return sql, params
  62. def date_interval_sql(self, timedelta):
  63. return "INTERVAL '%d 0:0:%d:%d' DAY_MICROSECOND" % (
  64. timedelta.days, timedelta.seconds, timedelta.microseconds), []
  65. def format_for_duration_arithmetic(self, sql):
  66. if self.connection.features.supports_microsecond_precision:
  67. return 'INTERVAL %s MICROSECOND' % sql
  68. else:
  69. return 'INTERVAL FLOOR(%s / 1000000) SECOND' % sql
  70. def drop_foreignkey_sql(self):
  71. return "DROP FOREIGN KEY"
  72. def force_no_ordering(self):
  73. """
  74. "ORDER BY NULL" prevents MySQL from implicitly ordering by grouped
  75. columns. If no ordering would otherwise be applied, we don't want any
  76. implicit sorting going on.
  77. """
  78. return [(None, ("NULL", [], False))]
  79. def fulltext_search_sql(self, field_name):
  80. return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
  81. def last_executed_query(self, cursor, sql, params):
  82. # With MySQLdb, cursor objects have an (undocumented) "_last_executed"
  83. # attribute where the exact query sent to the database is saved.
  84. # See MySQLdb/cursors.py in the source distribution.
  85. return force_text(getattr(cursor, '_last_executed', None), errors='replace')
  86. def no_limit_value(self):
  87. # 2**64 - 1, as recommended by the MySQL documentation
  88. return 18446744073709551615
  89. def quote_name(self, name):
  90. if name.startswith("`") and name.endswith("`"):
  91. return name # Quoting once is enough.
  92. return "`%s`" % name
  93. def random_function_sql(self):
  94. return 'RAND()'
  95. def sql_flush(self, style, tables, sequences, allow_cascade=False):
  96. # NB: The generated SQL below is specific to MySQL
  97. # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
  98. # to clear all tables of all data
  99. if tables:
  100. sql = ['SET FOREIGN_KEY_CHECKS = 0;']
  101. for table in tables:
  102. sql.append('%s %s;' % (
  103. style.SQL_KEYWORD('TRUNCATE'),
  104. style.SQL_FIELD(self.quote_name(table)),
  105. ))
  106. sql.append('SET FOREIGN_KEY_CHECKS = 1;')
  107. sql.extend(self.sequence_reset_by_name_sql(style, sequences))
  108. return sql
  109. else:
  110. return []
  111. def validate_autopk_value(self, value):
  112. # MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
  113. if value == 0:
  114. raise ValueError('The database backend does not accept 0 as a '
  115. 'value for AutoField.')
  116. return value
  117. def adapt_datetimefield_value(self, value):
  118. if value is None:
  119. return None
  120. # MySQL doesn't support tz-aware datetimes
  121. if timezone.is_aware(value):
  122. if settings.USE_TZ:
  123. value = timezone.make_naive(value, self.connection.timezone)
  124. else:
  125. raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.")
  126. if not self.connection.features.supports_microsecond_precision:
  127. value = value.replace(microsecond=0)
  128. return six.text_type(value)
  129. def adapt_timefield_value(self, value):
  130. if value is None:
  131. return None
  132. # MySQL doesn't support tz-aware times
  133. if timezone.is_aware(value):
  134. raise ValueError("MySQL backend does not support timezone-aware times.")
  135. return six.text_type(value)
  136. def max_name_length(self):
  137. return 64
  138. def bulk_insert_sql(self, fields, placeholder_rows):
  139. placeholder_rows_sql = (", ".join(row) for row in placeholder_rows)
  140. values_sql = ", ".join("(%s)" % sql for sql in placeholder_rows_sql)
  141. return "VALUES " + values_sql
  142. def combine_expression(self, connector, sub_expressions):
  143. """
  144. MySQL requires special cases for ^ operators in query expressions
  145. """
  146. if connector == '^':
  147. return 'POW(%s)' % ','.join(sub_expressions)
  148. return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
  149. def get_db_converters(self, expression):
  150. converters = super(DatabaseOperations, self).get_db_converters(expression)
  151. internal_type = expression.output_field.get_internal_type()
  152. if internal_type == 'TextField':
  153. converters.append(self.convert_textfield_value)
  154. elif internal_type in ['BooleanField', 'NullBooleanField']:
  155. converters.append(self.convert_booleanfield_value)
  156. elif internal_type == 'DateTimeField':
  157. converters.append(self.convert_datetimefield_value)
  158. elif internal_type == 'UUIDField':
  159. converters.append(self.convert_uuidfield_value)
  160. return converters
  161. def convert_textfield_value(self, value, expression, connection, context):
  162. if value is not None:
  163. value = force_text(value)
  164. return value
  165. def convert_booleanfield_value(self, value, expression, connection, context):
  166. if value in (0, 1):
  167. value = bool(value)
  168. return value
  169. def convert_datetimefield_value(self, value, expression, connection, context):
  170. if value is not None:
  171. if settings.USE_TZ:
  172. value = timezone.make_aware(value, self.connection.timezone)
  173. return value
  174. def convert_uuidfield_value(self, value, expression, connection, context):
  175. if value is not None:
  176. value = uuid.UUID(value)
  177. return value