diff -r 6641e941ef1e -r ff1a9aa48cfd app/django/db/backends/mysql/base.py --- 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):