text: """
  MySQL database backend for Django.
  
  Requires MySQLdb: http://sourceforge.net/projects/mysql-python
  """
  
  import re
  import sys
  
  try:
      import MySQLdb as Database
  except ImportError, e:
      from django.core.exceptions import ImproperlyConfigured
      raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
  
  # We want version (1, 2, 1, 'final', 2) or later. We can't just use
  # lexicographic ordering in this check because then (1, 2, 1, 'gamma')
  # inadvertently passes the version test.
  version = Database.version_info
  if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
          (len(version) < 5 or version[3] != 'final' or version[4] < 2))):
      from django.core.exceptions import ImproperlyConfigured
      raise ImproperlyConfigured("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__)
  
  from MySQLdb.converters import conversions
  from MySQLdb.constants import FIELD_TYPE, FLAG, CLIENT
  
  from django.db import utils
  from django.db.backends import *
  from django.db.backends.signals import connection_created
  from django.db.backends.mysql.client import DatabaseClient
  from django.db.backends.mysql.creation import DatabaseCreation
  from django.db.backends.mysql.introspection import DatabaseIntrospection
  from django.db.backends.mysql.validation import DatabaseValidation
  from django.utils.safestring import SafeString, SafeUnicode
  
  # Raise exceptions for database warnings if DEBUG is on
  from django.conf import settings
  if settings.DEBUG:
      from warnings import filterwarnings
      filterwarnings("error", category=Database.Warning)
  
  DatabaseError = Database.DatabaseError
  IntegrityError = Database.IntegrityError
  
  # MySQLdb-1.2.1 returns TIME columns as timedelta -- they are more like
  # timedelta in terms of actual behavior as they are signed and include days --
  # and Django expects time, so we still need to override that. We also need to
  # add special handling for SafeUnicode and SafeString as MySQLdb's type
  # checking is too tight to catch those (see Django ticket #6052).
  django_conversions = conversions.copy()
  django_conversions.update({
      FIELD_TYPE.TIME: util.typecast_time,
      FIELD_TYPE.DECIMAL: util.typecast_decimal,
      FIELD_TYPE.NEWDECIMAL: util.typecast_decimal,
  })
  
  # This should match the numerical portion of the version numbers (we can treat
  # versions like 5.0.24 and 5.0.24a as the same). Based on the list of version
  # at http://dev.mysql.com/doc/refman/4.1/en/news.html and
  # http://dev.mysql.com/doc/refman/5.0/en/news.html .
  server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
  
  # MySQLdb-1.2.1 and newer automatically makes use of SHOW WARNINGS on
  # MySQL-4.1 and newer, so the MysqlDebugWrapper is unnecessary. Since the
  # point is to raise Warnings as exceptions, this can be done with the Python
  # warning module, and this is setup when the connection is created, and the
  # standard util.CursorDebugWrapper can be used. Also, using sql_mode
  # TRADITIONAL will automatically cause most warnings to be treated as errors.
  
  class CursorWrapper(object):
      """
      A thin wrapper around MySQLdb's normal cursor class so that we can catch
      particular exception instances and reraise them with the right types.
  
      Implemented as a wrapper, rather than a subclass, so that we aren't stuck
      to the particular underlying representation returned by Connection.cursor().
      """
      codes_for_integrityerror = (1048,)
  
      def __init__(self, cursor):
          self.cursor = cursor
  
      def execute(self, query, args=None):
          try:
              return self.cursor.execute(query, args)
          except Database.IntegrityError, e:
              raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
          except Database.OperationalError, e:
              # Map some error codes to IntegrityError, since they seem to be
              # misclassified and Django would prefer the more logical place.
              if e[0] in self.codes_for_integrityerror:
                  raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
              raise
          except Database.DatabaseError, e:
              raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
  
      def executemany(self, query, args):
          try:
              return self.cursor.executemany(query, args)
          except Database.IntegrityError, e:
              raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
          except Database.OperationalError, e:
              # Map some error codes to IntegrityError, since they seem to be
              # misclassified and Django would prefer the more logical place.
              if e[0] in self.codes_for_integrityerror:
                  raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
              raise
          except Database.DatabaseError, e:
              raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
  
      def __getattr__(self, attr):
          if attr in self.__dict__:
              return self.__dict__[attr]
          else:
              return getattr(self.cursor, attr)
  
      def __iter__(self):
          return iter(self.cursor)
  
  class DatabaseFeatures(BaseDatabaseFeatures):
      empty_fetchmany_value = ()
      update_can_self_select = False
      allows_group_by_pk = True
      related_fields_match_type = True
      allow_sliced_subqueries = False
      supports_forward_references = False
      supports_long_model_names = False
      supports_microsecond_precision = False
      supports_regex_backreferencing = False
      supports_date_lookup_using_string = False
      supports_timezones = False
      requires_explicit_null_ordering_when_grouping = True
      allows_primary_key_0 = False
  
  class DatabaseOperations(BaseDatabaseOperations):
      compiler_module = "django.db.backends.mysql.compiler"
  
      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 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 ["NULL"]
  
      def fulltext_search_sql(self, field_name):
          return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
  
      def no_limit_value(self):
          # 2**64 - 1, as recommended by the MySQL documentation
          return 18446744073709551615L
  
      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):
          # 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;')
  
              # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
              # to reset sequence indices
              sql.extend(["%s %s %s %s %s;" % \
                  (style.SQL_KEYWORD('ALTER'),
                   style.SQL_KEYWORD('TABLE'),
                   style.SQL_TABLE(self.quote_name(sequence['table'])),
                   style.SQL_KEYWORD('AUTO_INCREMENT'),
                   style.SQL_FIELD('= 1'),
                  ) for sequence in sequences])
              return sql
          else:
              return []
  
      def value_to_db_datetime(self, value):
          if value is None:
              return None
  
          # MySQL doesn't support tz-aware datetimes
          if value.tzinfo is not None:
              raise ValueError("MySQL backend does not support timezone-aware datetimes.")
  
          # MySQL doesn't support microseconds
          return unicode(value.replace(microsecond=0))
  
      def value_to_db_time(self, value):
          if value is None:
              return None
  
          # MySQL doesn't support tz-aware datetimes
          if value.tzinfo is not None:
              raise ValueError("MySQL backend does not support timezone-aware datetimes.")
  
          # MySQL doesn't support microseconds
          return unicode(value.replace(microsecond=0))
  
      def year_lookup_bounds(self, value):
          # Again, no microseconds
          first = '%s-01-01 00:00:00'
          second = '%s-12-31 23:59:59.99'
          return [first % value, second % value]
  
      def max_name_length(self):
          return 64
  
  class DatabaseWrapper(BaseDatabaseWrapper):
      vendor = 'mysql'
      operators = {
          'exact': '= %s',
          'iexact': 'LIKE %s',
          'contains': 'LIKE BINARY %s',
          'icontains': 'LIKE %s',
          'regex': 'REGEXP BINARY %s',
          'iregex': 'REGEXP %s',
          'gt': '> %s',
          'gte': '>= %s',
          'lt': '< %s',
          'lte': '<= %s',
          'startswith': 'LIKE BINARY %s',
          'endswith': 'LIKE BINARY %s',
          'istartswith': 'LIKE %s',
          'iendswith': 'LIKE %s',
      }
  
      def __init__(self, *args, **kwargs):
          super(DatabaseWrapper, self).__init__(*args, **kwargs)
  
          self.server_version = None
          self.features = DatabaseFeatures(self)
          self.ops = DatabaseOperations()
          self.client = DatabaseClient(self)
          self.creation = DatabaseCreation(self)
          self.introspection = DatabaseIntrospection(self)
          self.validation = DatabaseValidation(self)
  
      def _valid_connection(self):
          if self.connection is not None:
              try:
                  self.connection.ping()
                  return True
              except DatabaseError:
                  self.connection.close()
                  self.connection = None
          return False
  
      def _cursor(self):
          if not self._valid_connection():
              kwargs = {
                  'conv': django_conversions,
                  'charset': 'utf8',
                  'use_unicode': True,
              }
              settings_dict = self.settings_dict
              if settings_dict['USER']:
                  kwargs['user'] = settings_dict['USER']
              if settings_dict['NAME']:
                  kwargs['db'] = settings_dict['NAME']
              if settings_dict['PASSWORD']:
                  kwargs['passwd'] = settings_dict['PASSWORD']
              if settings_dict['HOST'].startswith('/'):
                  kwargs['unix_socket'] = settings_dict['HOST']
              elif settings_dict['HOST']:
                  kwargs['host'] = settings_dict['HOST']
              if settings_dict['PORT']:
                  kwargs['port'] = int(settings_dict['PORT'])
              # We need the number of potentially affected rows after an
              # "UPDATE", not the number of changed rows.
              kwargs['client_flag'] = CLIENT.FOUND_ROWS
              kwargs.update(settings_dict['OPTIONS'])
              self.connection = Database.connect(**kwargs)
              self.connection.encoders[SafeUnicode] = self.connection.encoders[unicode]
              self.connection.encoders[SafeString] = self.connection.encoders[str]
              connection_created.send(sender=self.__class__, connection=self)
          cursor = CursorWrapper(self.connection.cursor())
          return cursor
  
      def _rollback(self):
          try:
              BaseDatabaseWrapper._rollback(self)
          except Database.NotSupportedError:
              pass
  
      def get_server_version(self):
          if not self.server_version:
              if not self._valid_connection():
                  self.cursor()
              m = server_version_re.match(self.connection.get_server_info())
              if not m:
                  raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
              self.server_version = tuple([int(x) for x in m.groups()])
          return self.server_version
  