app/django/db/backends/mysql/base.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
     2 MySQL database backend for Django.
     2 MySQL database backend for Django.
     3 
     3 
     4 Requires MySQLdb: http://sourceforge.net/projects/mysql-python
     4 Requires MySQLdb: http://sourceforge.net/projects/mysql-python
     5 """
     5 """
     6 
     6 
     7 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
     7 import re
       
     8 
     8 try:
     9 try:
     9     import MySQLdb as Database
    10     import MySQLdb as Database
    10 except ImportError, e:
    11 except ImportError, e:
    11     from django.core.exceptions import ImproperlyConfigured
    12     from django.core.exceptions import ImproperlyConfigured
    12     raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
    13     raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
    19         (len(version) < 5 or version[3] != 'final' or version[4] < 2))):
    20         (len(version) < 5 or version[3] != 'final' or version[4] < 2))):
    20     from django.core.exceptions import ImproperlyConfigured
    21     from django.core.exceptions import ImproperlyConfigured
    21     raise ImproperlyConfigured("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__)
    22     raise ImproperlyConfigured("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__)
    22 
    23 
    23 from MySQLdb.converters import conversions
    24 from MySQLdb.converters import conversions
    24 from MySQLdb.constants import FIELD_TYPE
    25 from MySQLdb.constants import FIELD_TYPE, FLAG
    25 import re
    26 
       
    27 from django.db.backends import *
       
    28 from django.db.backends.mysql.client import DatabaseClient
       
    29 from django.db.backends.mysql.creation import DatabaseCreation
       
    30 from django.db.backends.mysql.introspection import DatabaseIntrospection
       
    31 from django.db.backends.mysql.validation import DatabaseValidation
    26 
    32 
    27 # Raise exceptions for database warnings if DEBUG is on
    33 # Raise exceptions for database warnings if DEBUG is on
    28 from django.conf import settings
    34 from django.conf import settings
    29 if settings.DEBUG:
    35 if settings.DEBUG:
    30     from warnings import filterwarnings
    36     from warnings import filterwarnings
    57 # point is to raise Warnings as exceptions, this can be done with the Python
    63 # point is to raise Warnings as exceptions, this can be done with the Python
    58 # warning module, and this is setup when the connection is created, and the
    64 # warning module, and this is setup when the connection is created, and the
    59 # standard util.CursorDebugWrapper can be used. Also, using sql_mode
    65 # standard util.CursorDebugWrapper can be used. Also, using sql_mode
    60 # TRADITIONAL will automatically cause most warnings to be treated as errors.
    66 # TRADITIONAL will automatically cause most warnings to be treated as errors.
    61 
    67 
       
    68 class CursorWrapper(object):
       
    69     """
       
    70     A thin wrapper around MySQLdb's normal cursor class so that we can catch
       
    71     particular exception instances and reraise them with the right types.
       
    72 
       
    73     Implemented as a wrapper, rather than a subclass, so that we aren't stuck
       
    74     to the particular underlying representation returned by Connection.cursor().
       
    75     """
       
    76     codes_for_integrityerror = (1048,)
       
    77 
       
    78     def __init__(self, cursor):
       
    79         self.cursor = cursor
       
    80 
       
    81     def execute(self, query, args=None):
       
    82         try:
       
    83             return self.cursor.execute(query, args)
       
    84         except Database.OperationalError, e:
       
    85             # Map some error codes to IntegrityError, since they seem to be
       
    86             # misclassified and Django would prefer the more logical place.
       
    87             if e[0] in self.codes_for_integrityerror:
       
    88                 raise Database.IntegrityError(tuple(e))
       
    89             raise
       
    90 
       
    91     def executemany(self, query, args):
       
    92         try:
       
    93             return self.cursor.executemany(query, args)
       
    94         except Database.OperationalError, e:
       
    95             # Map some error codes to IntegrityError, since they seem to be
       
    96             # misclassified and Django would prefer the more logical place.
       
    97             if e[0] in self.codes_for_integrityerror:
       
    98                 raise Database.IntegrityError(tuple(e))
       
    99             raise
       
   100 
       
   101     def __getattr__(self, attr):
       
   102         if attr in self.__dict__:
       
   103             return self.__dict__[attr]
       
   104         else:
       
   105             return getattr(self.cursor, attr)
       
   106 
       
   107     def __iter__(self):
       
   108         return iter(self.cursor)
       
   109 
    62 class DatabaseFeatures(BaseDatabaseFeatures):
   110 class DatabaseFeatures(BaseDatabaseFeatures):
    63     autoindexes_primary_keys = False
       
    64     inline_fk_references = False
       
    65     empty_fetchmany_value = ()
   111     empty_fetchmany_value = ()
    66     update_can_self_select = False
   112     update_can_self_select = False
       
   113     related_fields_match_type = True
    67 
   114 
    68 class DatabaseOperations(BaseDatabaseOperations):
   115 class DatabaseOperations(BaseDatabaseOperations):
    69     def date_extract_sql(self, lookup_type, field_name):
   116     def date_extract_sql(self, lookup_type, field_name):
    70         # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
   117         # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
    71         return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
   118         return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
    86     def drop_foreignkey_sql(self):
   133     def drop_foreignkey_sql(self):
    87         return "DROP FOREIGN KEY"
   134         return "DROP FOREIGN KEY"
    88 
   135 
    89     def fulltext_search_sql(self, field_name):
   136     def fulltext_search_sql(self, field_name):
    90         return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
   137         return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
    91 
       
    92     def limit_offset_sql(self, limit, offset=None):
       
    93         # 'LIMIT 20,40'
       
    94         sql = "LIMIT "
       
    95         if offset and offset != 0:
       
    96             sql += "%s," % offset
       
    97         return sql + str(limit)
       
    98 
   138 
    99     def no_limit_value(self):
   139     def no_limit_value(self):
   100         # 2**64 - 1, as recommended by the MySQL documentation
   140         # 2**64 - 1, as recommended by the MySQL documentation
   101         return 18446744073709551615L
   141         return 18446744073709551615L
   102 
   142 
   129                 ) for sequence in sequences])
   169                 ) for sequence in sequences])
   130             return sql
   170             return sql
   131         else:
   171         else:
   132             return []
   172             return []
   133 
   173 
       
   174     def value_to_db_datetime(self, value):
       
   175         if value is None:
       
   176             return None
       
   177         
       
   178         # MySQL doesn't support tz-aware datetimes
       
   179         if value.tzinfo is not None:
       
   180             raise ValueError("MySQL backend does not support timezone-aware datetimes.")
       
   181 
       
   182         # MySQL doesn't support microseconds
       
   183         return unicode(value.replace(microsecond=0))
       
   184 
       
   185     def value_to_db_time(self, value):
       
   186         if value is None:
       
   187             return None
       
   188             
       
   189         # MySQL doesn't support tz-aware datetimes
       
   190         if value.tzinfo is not None:
       
   191             raise ValueError("MySQL backend does not support timezone-aware datetimes.")
       
   192         
       
   193         # MySQL doesn't support microseconds
       
   194         return unicode(value.replace(microsecond=0))
       
   195 
       
   196     def year_lookup_bounds(self, value):
       
   197         # Again, no microseconds
       
   198         first = '%s-01-01 00:00:00'
       
   199         second = '%s-12-31 23:59:59.99'
       
   200         return [first % value, second % value]
       
   201 
   134 class DatabaseWrapper(BaseDatabaseWrapper):
   202 class DatabaseWrapper(BaseDatabaseWrapper):
   135     features = DatabaseFeatures()
   203 
   136     ops = DatabaseOperations()
       
   137     operators = {
   204     operators = {
   138         'exact': '= %s',
   205         'exact': '= %s',
   139         'iexact': 'LIKE %s',
   206         'iexact': 'LIKE %s',
   140         'contains': 'LIKE BINARY %s',
   207         'contains': 'LIKE BINARY %s',
   141         'icontains': 'LIKE %s',
   208         'icontains': 'LIKE %s',
   152     }
   219     }
   153 
   220 
   154     def __init__(self, **kwargs):
   221     def __init__(self, **kwargs):
   155         super(DatabaseWrapper, self).__init__(**kwargs)
   222         super(DatabaseWrapper, self).__init__(**kwargs)
   156         self.server_version = None
   223         self.server_version = None
       
   224 
       
   225         self.features = DatabaseFeatures()
       
   226         self.ops = DatabaseOperations()
       
   227         self.client = DatabaseClient()
       
   228         self.creation = DatabaseCreation(self)
       
   229         self.introspection = DatabaseIntrospection(self)
       
   230         self.validation = DatabaseValidation()
   157 
   231 
   158     def _valid_connection(self):
   232     def _valid_connection(self):
   159         if self.connection is not None:
   233         if self.connection is not None:
   160             try:
   234             try:
   161                 self.connection.ping()
   235                 self.connection.ping()
   184                 kwargs['host'] = settings.DATABASE_HOST
   258                 kwargs['host'] = settings.DATABASE_HOST
   185             if settings.DATABASE_PORT:
   259             if settings.DATABASE_PORT:
   186                 kwargs['port'] = int(settings.DATABASE_PORT)
   260                 kwargs['port'] = int(settings.DATABASE_PORT)
   187             kwargs.update(self.options)
   261             kwargs.update(self.options)
   188             self.connection = Database.connect(**kwargs)
   262             self.connection = Database.connect(**kwargs)
   189         cursor = self.connection.cursor()
   263         cursor = CursorWrapper(self.connection.cursor())
   190         return cursor
   264         return cursor
   191 
   265 
   192     def _rollback(self):
   266     def _rollback(self):
   193         try:
   267         try:
   194             BaseDatabaseWrapper._rollback(self)
   268             BaseDatabaseWrapper._rollback(self)