Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 207 lines (180 sloc) 7.966 kB
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
1 """
2 MySQL database backend for Django.
3
4 Requires MySQLdb: http://sourceforge.net/projects/mysql-python
5 """
6
38b5d7f @adrianholovaty Began implementing BaseDatabaseOperations class for every database ba…
adrianholovaty authored
7 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
8e9833f @adrianholovaty Fixed #1673 -- Every database backend now raises ImproperlyConfigured…
adrianholovaty authored
8 try:
9 import MySQLdb as Database
10 except ImportError, e:
11 from django.core.exceptions import ImproperlyConfigured
12 raise ImproperlyConfigured, "Error loading MySQLdb module: %s" % e
cb624b1 @malcolmt Fixed #3747 -- Added a stricter MySQLdb version check so that (1, 2, 1,
malcolmt authored
13
14 # We want version (1, 2, 1, 'final', 2) or later. We can't just use
15 # lexicographic ordering in this check because then (1, 2, 1, 'gamma')
16 # inadvertently passes the version test.
17 version = Database.version_info
92c35a0 @malcolmt Fixed #2365, #3324 -- Renamed FloatField to DecimalField and changed …
malcolmt authored
18 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
cb624b1 @malcolmt Fixed #3747 -- Added a stricter MySQLdb version check so that (1, 2, 1,
malcolmt authored
19 (len(version) < 5 or version[3] != 'final' or version[4] < 2))):
20 raise ImportError, "MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
21
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
22 from MySQLdb.converters import conversions
23 from MySQLdb.constants import FIELD_TYPE
24 import types
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
25 import re
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
26
27 DatabaseError = Database.DatabaseError
b3e0b59 @malcolmt Fixed #3450 -- Exposed IntegrityError in a backend-neutral fashion. T…
malcolmt authored
28 IntegrityError = Database.IntegrityError
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
29
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
30 # MySQLdb-1.2.1 supports the Python boolean type, and only uses datetime
31 # module for time-related columns; older versions could have used mx.DateTime
32 # or strings if there were no datetime module. However, MySQLdb still returns
33 # TIME columns as timedelta -- they are more like timedelta in terms of actual
34 # behavior as they are signed and include days -- and Django expects time, so
35 # we still need to override that.
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
36 django_conversions = conversions.copy()
37 django_conversions.update({
38 FIELD_TYPE.TIME: util.typecast_time,
92c35a0 @malcolmt Fixed #2365, #3324 -- Renamed FloatField to DecimalField and changed …
malcolmt authored
39 FIELD_TYPE.DECIMAL: util.typecast_decimal,
40 FIELD_TYPE.NEWDECIMAL: util.typecast_decimal,
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
41 })
42
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
43 # This should match the numerical portion of the version numbers (we can treat
44 # versions like 5.0.24 and 5.0.24a as the same). Based on the list of version
45 # at http://dev.mysql.com/doc/refman/4.1/en/news.html and
46 # http://dev.mysql.com/doc/refman/5.0/en/news.html .
47 server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
48
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
49 # MySQLdb-1.2.1 and newer automatically makes use of SHOW WARNINGS on
50 # MySQL-4.1 and newer, so the MysqlDebugWrapper is unnecessary. Since the
51 # point is to raise Warnings as exceptions, this can be done with the Python
52 # warning module, and this is setup when the connection is created, and the
53 # standard util.CursorDebugWrapper can be used. Also, using sql_mode
54 # TRADITIONAL will automatically cause most warnings to be treated as errors.
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
55
38b5d7f @adrianholovaty Began implementing BaseDatabaseOperations class for every database ba…
adrianholovaty authored
56 class DatabaseOperations(BaseDatabaseOperations):
aab04a4 @adrianholovaty Refactored get_date_extract_sql() to DatabaseOperations.date_extract_…
adrianholovaty authored
57 def date_extract_sql(self, lookup_type, field_name):
58 # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
59 return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
38b5d7f @adrianholovaty Began implementing BaseDatabaseOperations class for every database ba…
adrianholovaty authored
60
5f51f05 @adrianholovaty Refactored get_date_trunc_sql() to DatabaseOperations.date_trunc_sql(…
adrianholovaty authored
61 def date_trunc_sql(self, lookup_type, field_name):
62 fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
63 format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
64 format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
65 try:
66 i = fields.index(lookup_type) + 1
67 except ValueError:
68 sql = field_name
69 else:
70 format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
71 sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
72 return sql
73
23a736d @adrianholovaty Refactored get_drop_foreignkey_sql() to DatabaseOperations.drop_forei…
adrianholovaty authored
74 def drop_foreignkey_sql(self):
75 return "DROP FOREIGN KEY"
76
5a64264 @adrianholovaty Refactored get_fulltext_search_sql() to DatabaseOperations.fulltext_s…
adrianholovaty authored
77 def fulltext_search_sql(self, field_name):
78 return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
79
d3e69c3 @adrianholovaty Refactored get_limit_offset_sql() to DatabaseOperations.limit_offset_…
adrianholovaty authored
80 def limit_offset_sql(self, limit, offset=None):
81 # 'LIMIT 20,40'
82 sql = "LIMIT "
83 if offset and offset != 0:
84 sql += "%s," % offset
85 return sql + str(limit)
86
221f99e @adrianholovaty Refactored quote_name() to DatabaseOperations.quote_name(). Refs #5106
adrianholovaty authored
87 def quote_name(self, name):
88 if name.startswith("`") and name.endswith("`"):
89 return name # Quoting once is enough.
90 return "`%s`" % name
91
c44fb66 @adrianholovaty Refactored get_random_function_sql() to DatabaseOperations.random_fun…
adrianholovaty authored
92 def random_function_sql(self):
93 return 'RAND()'
94
aaed6e0 @adrianholovaty Refactored get_sql_flush() to DatabaseOperations.sql_flush(). Refs #5106
adrianholovaty authored
95 def sql_flush(self, style, tables, sequences):
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:
221f99e @adrianholovaty Refactored quote_name() to DatabaseOperations.quote_name(). Refs #5106
adrianholovaty authored
102 sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
aaed6e0 @adrianholovaty Refactored get_sql_flush() to DatabaseOperations.sql_flush(). Refs #5106
adrianholovaty authored
103 sql.append('SET FOREIGN_KEY_CHECKS = 1;')
104
105 # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
106 # to reset sequence indices
107 sql.extend(["%s %s %s %s %s;" % \
108 (style.SQL_KEYWORD('ALTER'),
109 style.SQL_KEYWORD('TABLE'),
221f99e @adrianholovaty Refactored quote_name() to DatabaseOperations.quote_name(). Refs #5106
adrianholovaty authored
110 style.SQL_TABLE(self.quote_name(sequence['table'])),
aaed6e0 @adrianholovaty Refactored get_sql_flush() to DatabaseOperations.sql_flush(). Refs #5106
adrianholovaty authored
111 style.SQL_KEYWORD('AUTO_INCREMENT'),
112 style.SQL_FIELD('= 1'),
113 ) for sequence in sequences])
114 return sql
115 else:
116 return []
117
7c41b19 @adrianholovaty Refactored all database backends to inherit from a common base class …
adrianholovaty authored
118 class DatabaseWrapper(BaseDatabaseWrapper):
38b5d7f @adrianholovaty Began implementing BaseDatabaseOperations class for every database ba…
adrianholovaty authored
119 ops = DatabaseOperations()
120
fef89a0 @jacobian Fixed #2866: Added DATABASE_OPTIONS setting which gets passed as extr…
jacobian authored
121 def __init__(self, **kwargs):
7c41b19 @adrianholovaty Refactored all database backends to inherit from a common base class …
adrianholovaty authored
122 super(DatabaseWrapper, self).__init__(**kwargs)
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
123 self.server_version = None
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
124
125 def _valid_connection(self):
126 if self.connection is not None:
127 try:
128 self.connection.ping()
129 return True
130 except DatabaseError:
131 self.connection.close()
132 self.connection = None
133 return False
134
7c41b19 @adrianholovaty Refactored all database backends to inherit from a common base class …
adrianholovaty authored
135 def _cursor(self, settings):
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
136 from warnings import filterwarnings
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
137 if not self._valid_connection():
138 kwargs = {
139 'conv': django_conversions,
1f9711f @malcolmt Fixed #3754 -- Re-introduced utf-8 as default encoding for interactio…
malcolmt authored
140 'charset': 'utf8',
953badb @malcolmt Merged Unicode branch into trunk (r4952:5608). This should be fully
malcolmt authored
141 'use_unicode': True,
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
142 }
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
143 if settings.DATABASE_USER:
144 kwargs['user'] = settings.DATABASE_USER
145 if settings.DATABASE_NAME:
146 kwargs['db'] = settings.DATABASE_NAME
147 if settings.DATABASE_PASSWORD:
148 kwargs['passwd'] = settings.DATABASE_PASSWORD
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
149 if settings.DATABASE_HOST.startswith('/'):
150 kwargs['unix_socket'] = settings.DATABASE_HOST
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
151 elif settings.DATABASE_HOST:
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
152 kwargs['host'] = settings.DATABASE_HOST
153 if settings.DATABASE_PORT:
154 kwargs['port'] = int(settings.DATABASE_PORT)
fef89a0 @jacobian Fixed #2866: Added DATABASE_OPTIONS setting which gets passed as extr…
jacobian authored
155 kwargs.update(self.options)
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
156 self.connection = Database.connect(**kwargs)
7c41b19 @adrianholovaty Refactored all database backends to inherit from a common base class …
adrianholovaty authored
157 cursor = self.connection.cursor()
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
158 if settings.DEBUG:
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
159 filterwarnings("error", category=Database.Warning)
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
160 return cursor
161
162 def _rollback(self):
7c41b19 @adrianholovaty Refactored all database backends to inherit from a common base class …
adrianholovaty authored
163 try:
164 BaseDatabaseWrapper._rollback(self)
165 except Database.NotSupportedError:
166 pass
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
167
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
168 def get_server_version(self):
169 if not self.server_version:
170 if not self._valid_connection():
171 self.cursor()
172 m = server_version_re.match(self.connection.get_server_info())
173 if not m:
174 raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
a834f21 @malcolmt Fixed omission in [3872].
malcolmt authored
175 self.server_version = tuple([int(x) for x in m.groups()])
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
176 return self.server_version
177
ac64e91 @malcolmt Merged boulder-oracle-sprint branch (r3965:5512) back into trunk. All
malcolmt authored
178 allows_group_by_ordinal = True
179 allows_unique_and_pk = True
180 autoindexes_primary_keys = False
181 needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
182 needs_upper_for_iops = False
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
183 supports_constraints = True
ac64e91 @malcolmt Merged boulder-oracle-sprint branch (r3965:5512) back into trunk. All
malcolmt authored
184 supports_tablespaces = False
185 uses_case_insensitive_names = False
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
186
187 dictfetchone = util.dictfetchone
188 dictfetchmany = util.dictfetchmany
189 dictfetchall = util.dictfetchall
190
191 OPERATOR_MAPPING = {
192 'exact': '= %s',
193 'iexact': 'LIKE %s',
194 'contains': 'LIKE BINARY %s',
195 'icontains': 'LIKE %s',
24512a7 @jacobian Fixed #1465: added support for regex lookups. Thanks, Tom Tobin.
jacobian authored
196 'regex': 'REGEXP BINARY %s',
197 'iregex': 'REGEXP %s',
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
198 'gt': '> %s',
199 'gte': '>= %s',
200 'lt': '< %s',
201 'lte': '<= %s',
202 'startswith': 'LIKE BINARY %s',
203 'endswith': 'LIKE BINARY %s',
204 'istartswith': 'LIKE %s',
205 'iendswith': 'LIKE %s',
206 }
Something went wrong with that request. Please try again.