--- a/app/django/db/backends/mysql/base.py Tue Oct 14 12:36:55 2008 +0000
+++ b/app/django/db/backends/mysql/base.py Tue Oct 14 16:00:59 2008 +0000
@@ -4,7 +4,8 @@
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
"""
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
+import re
+
try:
import MySQLdb as Database
except ImportError, e:
@@ -21,8 +22,13 @@
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
-import re
+from MySQLdb.constants import FIELD_TYPE, FLAG
+
+from django.db.backends import *
+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
# Raise exceptions for database warnings if DEBUG is on
from django.conf import settings
@@ -59,11 +65,52 @@
# 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.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 Database.IntegrityError(tuple(e))
+ raise
+
+ def executemany(self, query, args):
+ try:
+ return self.cursor.executemany(query, args)
+ 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 Database.IntegrityError(tuple(e))
+ raise
+
+ 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):
- autoindexes_primary_keys = False
- inline_fk_references = False
empty_fetchmany_value = ()
update_can_self_select = False
+ related_fields_match_type = True
class DatabaseOperations(BaseDatabaseOperations):
def date_extract_sql(self, lookup_type, field_name):
@@ -89,13 +136,6 @@
def fulltext_search_sql(self, field_name):
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
- def limit_offset_sql(self, limit, offset=None):
- # 'LIMIT 20,40'
- sql = "LIMIT "
- if offset and offset != 0:
- sql += "%s," % offset
- return sql + str(limit)
-
def no_limit_value(self):
# 2**64 - 1, as recommended by the MySQL documentation
return 18446744073709551615L
@@ -131,9 +171,36 @@
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]
+
class DatabaseWrapper(BaseDatabaseWrapper):
- features = DatabaseFeatures()
- ops = DatabaseOperations()
+
operators = {
'exact': '= %s',
'iexact': 'LIKE %s',
@@ -155,6 +222,13 @@
super(DatabaseWrapper, self).__init__(**kwargs)
self.server_version = None
+ self.features = DatabaseFeatures()
+ self.ops = DatabaseOperations()
+ self.client = DatabaseClient()
+ self.creation = DatabaseCreation(self)
+ self.introspection = DatabaseIntrospection(self)
+ self.validation = DatabaseValidation()
+
def _valid_connection(self):
if self.connection is not None:
try:
@@ -186,7 +260,7 @@
kwargs['port'] = int(settings.DATABASE_PORT)
kwargs.update(self.options)
self.connection = Database.connect(**kwargs)
- cursor = self.connection.cursor()
+ cursor = CursorWrapper(self.connection.cursor())
return cursor
def _rollback(self):