Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 238 lines (204 sloc) 8.525 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
7 from django.db.backends import 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
18 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
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,
39 })
40
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
41 # This should match the numerical portion of the version numbers (we can treat
42 # versions like 5.0.24 and 5.0.24a as the same). Based on the list of version
43 # at http://dev.mysql.com/doc/refman/4.1/en/news.html and
44 # http://dev.mysql.com/doc/refman/5.0/en/news.html .
45 server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
46
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
47 # MySQLdb-1.2.1 and newer automatically makes use of SHOW WARNINGS on
48 # MySQL-4.1 and newer, so the MysqlDebugWrapper is unnecessary. Since the
49 # point is to raise Warnings as exceptions, this can be done with the Python
50 # warning module, and this is setup when the connection is created, and the
51 # standard util.CursorDebugWrapper can be used. Also, using sql_mode
52 # 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
53
54 try:
55 # Only exists in Python 2.4+
56 from threading import local
57 except ImportError:
58 # Import copy of _thread_local.py from Python 2.4
59 from django.utils._threading_local import local
60
61 class DatabaseWrapper(local):
fef89a0 @jacobian Fixed #2866: Added DATABASE_OPTIONS setting which gets passed as extr…
jacobian authored
62 def __init__(self, **kwargs):
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
63 self.connection = None
64 self.queries = []
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
65 self.server_version = None
fef89a0 @jacobian Fixed #2866: Added DATABASE_OPTIONS setting which gets passed as extr…
jacobian authored
66 self.options = kwargs
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
67
68 def _valid_connection(self):
69 if self.connection is not None:
70 try:
71 self.connection.ping()
72 return True
73 except DatabaseError:
74 self.connection.close()
75 self.connection = None
76 return False
77
78 def cursor(self):
79 from django.conf import settings
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
80 from warnings import filterwarnings
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
81 if not self._valid_connection():
82 kwargs = {
83 'conv': django_conversions,
1f9711f @malcolmt Fixed #3754 -- Re-introduced utf-8 as default encoding for interactio…
malcolmt authored
84 'charset': 'utf8',
85 'use_unicode': False,
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
86 }
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
87 if settings.DATABASE_USER:
88 kwargs['user'] = settings.DATABASE_USER
89 if settings.DATABASE_NAME:
90 kwargs['db'] = settings.DATABASE_NAME
91 if settings.DATABASE_PASSWORD:
92 kwargs['passwd'] = settings.DATABASE_PASSWORD
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
93 if settings.DATABASE_HOST.startswith('/'):
94 kwargs['unix_socket'] = settings.DATABASE_HOST
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
95 elif settings.DATABASE_HOST:
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
96 kwargs['host'] = settings.DATABASE_HOST
97 if settings.DATABASE_PORT:
98 kwargs['port'] = int(settings.DATABASE_PORT)
fef89a0 @jacobian Fixed #2866: Added DATABASE_OPTIONS setting which gets passed as extr…
jacobian authored
99 kwargs.update(self.options)
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
100 self.connection = Database.connect(**kwargs)
f21cbb4 @adrianholovaty Fixed #3151 -- Improved MySQL backend not to send 'SET NAMES utf8' be…
adrianholovaty authored
101 cursor = self.connection.cursor()
102 else:
103 cursor = self.connection.cursor()
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
104 if settings.DEBUG:
f9c4ce5 @malcolmt Fixed #2635 -- Added improved MySQL backend support from Andy Dustman…
malcolmt authored
105 filterwarnings("error", category=Database.Warning)
106 return util.CursorDebugWrapper(cursor, self)
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
107 return cursor
108
109 def _commit(self):
cc8d656 @malcolmt Fixed #3024 -- Fixed database commit() and rollback() behaviour so it…
malcolmt authored
110 if self.connection is not None:
111 self.connection.commit()
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
112
113 def _rollback(self):
cc8d656 @malcolmt Fixed #3024 -- Fixed database commit() and rollback() behaviour so it…
malcolmt authored
114 if self.connection is not None:
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
115 try:
116 self.connection.rollback()
117 except Database.NotSupportedError:
118 pass
119
120 def close(self):
121 if self.connection is not None:
122 self.connection.close()
123 self.connection = None
124
6068f3e @malcolmt Reintroduced the changes from [3855] with more flexible handling of v…
malcolmt authored
125 def get_server_version(self):
126 if not self.server_version:
127 if not self._valid_connection():
128 self.cursor()
129 m = server_version_re.match(self.connection.get_server_info())
130 if not m:
131 raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
a834f21 @malcolmt Fixed omission in [3872].
malcolmt authored
132 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
133 return self.server_version
134
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
135 supports_constraints = True
136
137 def quote_name(name):
138 if name.startswith("`") and name.endswith("`"):
139 return name # Quoting once is enough.
140 return "`%s`" % name
141
142 dictfetchone = util.dictfetchone
143 dictfetchmany = util.dictfetchmany
144 dictfetchall = util.dictfetchall
145
146 def get_last_insert_id(cursor, table_name, pk_name):
147 return cursor.lastrowid
148
149 def get_date_extract_sql(lookup_type, table_name):
150 # lookup_type is 'year', 'month', 'day'
151 # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
152 return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name)
153
154 def get_date_trunc_sql(lookup_type, field_name):
155 # lookup_type is 'year', 'month', 'day'
156 fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
157 format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
158 format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
159 try:
160 i = fields.index(lookup_type) + 1
161 except ValueError:
162 sql = field_name
163 else:
164 format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
165 sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
166 return sql
167
168 def get_limit_offset_sql(limit, offset=None):
169 sql = "LIMIT "
170 if offset and offset != 0:
171 sql += "%s," % offset
172 return sql + str(limit)
173
174 def get_random_function_sql():
175 return "RAND()"
176
51f39d5 @jacobian Fixed #3390: the serializer can now contain forward references. Thank…
jacobian authored
177 def get_deferrable_sql():
178 return ""
179
168429d @adrianholovaty Fixed #593 -- Added 'search' DB-API lookup type, which does full-text…
adrianholovaty authored
180 def get_fulltext_search_sql(field_name):
181 return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
182
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
183 def get_drop_foreignkey_sql():
184 return "DROP FOREIGN KEY"
185
7e2b888 @malcolmt Fixed #2108 -- allow saving of empty models, rather than just droppin…
malcolmt authored
186 def get_pk_default_value():
187 return "DEFAULT"
188
f2582eb @freakboy3742 Fixes #2333 -- Added test fixtures framework.
freakboy3742 authored
189 def get_sql_flush(style, tables, sequences):
190 """Return a list of SQL statements required to remove all data from
191 all tables in the database (without actually removing the tables
192 themselves) and put the database in an empty 'initial' state
193
194 """
195 # NB: The generated SQL below is specific to MySQL
196 # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
197 # to clear all tables of all data
198 if tables:
199 sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + \
200 ['%s %s;' % \
201 (style.SQL_KEYWORD('TRUNCATE'),
202 style.SQL_FIELD(quote_name(table))
203 ) for table in tables] + \
204 ['SET FOREIGN_KEY_CHECKS = 1;']
205
206 # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
207 # to reset sequence indices
208 sql.extend(["%s %s %s %s %s;" % \
209 (style.SQL_KEYWORD('ALTER'),
210 style.SQL_KEYWORD('TABLE'),
211 style.SQL_TABLE(quote_name(sequence['table'])),
212 style.SQL_KEYWORD('AUTO_INCREMENT'),
213 style.SQL_FIELD('= 1'),
214 ) for sequence in sequences])
215 return sql
216 else:
217 return []
218
dabd966 @freakboy3742 Fixed #3790 -- Fixed a problem with sequence resetting during fixture…
freakboy3742 authored
219 def get_sql_sequence_reset(style, model_list):
220 "Returns a list of the SQL statements to reset sequences for the given models."
221 # No sequence reset required
222 return []
223
f69cf70 @adrianholovaty MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards…
adrianholovaty authored
224 OPERATOR_MAPPING = {
225 'exact': '= %s',
226 'iexact': 'LIKE %s',
227 'contains': 'LIKE BINARY %s',
228 'icontains': 'LIKE %s',
229 'gt': '> %s',
230 'gte': '>= %s',
231 'lt': '< %s',
232 'lte': '<= %s',
233 'startswith': 'LIKE BINARY %s',
234 'endswith': 'LIKE BINARY %s',
235 'istartswith': 'LIKE %s',
236 'iendswith': 'LIKE %s',
237 }
Something went wrong with that request. Please try again.