diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index faaefca06b..c1ef625bb6 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -15,11 +15,13 @@ except ImportError, e: from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e) +from django.utils.functional import cached_property + # We want version (1, 2, 1, 'final', 2) or later. We can't just use # lexicographic ordering in this check because then (1, 2, 1, 'gamma') # inadvertently passes the version test. version = Database.version_info -if (version < (1,2,1) or (version[:3] == (1, 2, 1) and +if (version < (1, 2, 1) or (version[:3] == (1, 2, 1) and (len(version) < 5 or version[3] != 'final' or version[4] < 2))): from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__) @@ -163,6 +165,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_timezones = False requires_explicit_null_ordering_when_grouping = True allows_primary_key_0 = False + uses_savepoints = True def __init__(self, connection): super(DatabaseFeatures, self).__init__(connection) @@ -387,8 +390,6 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.connection = Database.connect(**kwargs) self.connection.encoders[SafeUnicode] = self.connection.encoders[unicode] self.connection.encoders[SafeString] = self.connection.encoders[str] - self.features.uses_savepoints = \ - self.get_server_version() >= (5, 0, 3) connection_created.send(sender=self.__class__, connection=self) cursor = self.connection.cursor() if new_connection: @@ -405,10 +406,11 @@ class DatabaseWrapper(BaseDatabaseWrapper): except Database.NotSupportedError: pass - def get_server_version(self): + @cached_property + def mysql_version(self): if not self.server_version: if not self._valid_connection(): - self.cursor() + self.cursor().close() m = server_version_re.match(self.connection.get_server_info()) if not m: raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info()) diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index ab4eebea90..bb56ae38a6 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -65,27 +65,14 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): key columns in given table. """ key_columns = [] - try: - cursor.execute(""" - SELECT column_name, referenced_table_name, referenced_column_name - FROM information_schema.key_column_usage - WHERE table_name = %s - AND table_schema = DATABASE() - AND referenced_table_name IS NOT NULL - AND referenced_column_name IS NOT NULL""", [table_name]) - key_columns.extend(cursor.fetchall()) - except (ProgrammingError, OperationalError): - # Fall back to "SHOW CREATE TABLE", for previous MySQL versions. - # Go through all constraints and save the equal matches. - cursor.execute("SHOW CREATE TABLE %s" % self.connection.ops.quote_name(table_name)) - for row in cursor.fetchall(): - pos = 0 - while True: - match = foreign_key_re.search(row[1], pos) - if match == None: - break - pos = match.end() - key_columns.append(match.groups()) + cursor.execute(""" + SELECT column_name, referenced_table_name, referenced_column_name + FROM information_schema.key_column_usage + WHERE table_name = %s + AND table_schema = DATABASE() + AND referenced_table_name IS NOT NULL + AND referenced_column_name IS NOT NULL""", [table_name]) + key_columns.extend(cursor.fetchall()) return key_columns def get_primary_key_column(self, cursor, table_name): diff --git a/django/db/backends/mysql/validation.py b/django/db/backends/mysql/validation.py index 663cc7da4e..de7474d1e5 100644 --- a/django/db/backends/mysql/validation.py +++ b/django/db/backends/mysql/validation.py @@ -3,30 +3,13 @@ from django.db.backends import BaseDatabaseValidation class DatabaseValidation(BaseDatabaseValidation): def validate_field(self, errors, opts, f): """ - There are some field length restrictions for MySQL: - - - Prior to version 5.0.3, character fields could not exceed 255 - characters in length. - - No character (varchar) fields can have a length exceeding 255 - characters if they have a unique index on them. + MySQL has the following field length restriction: + No character (varchar) fields can have a length exceeding 255 + characters if they have a unique index on them. """ from django.db import models - from MySQLdb import OperationalError - try: - db_version = self.connection.get_server_version() - text_version = '.'.join([str(n) for n in db_version[:3]]) - except OperationalError: - db_version = None - text_version = '' varchar_fields = (models.CharField, models.CommaSeparatedIntegerField, models.SlugField) - if isinstance(f, varchar_fields) and f.max_length > 255: - if db_version and db_version < (5, 0, 3): - msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %(version)s).' - elif f.unique == True: - msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when using "unique=True".' - else: - msg = None - - if msg: - errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__, 'version': text_version}) + if isinstance(f, varchar_fields) and f.max_length > 255 and f.unique: + msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when using "unique=True".' + errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__}) diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 59a0c36ad6..c271946bb7 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -122,30 +122,23 @@ lookups that use the ``LIKE`` operator in their SQL, as is done with the MySQL notes =========== -Django expects the database to support transactions, referential integrity, and -Unicode (UTF-8 encoding). Fortunately, MySQL_ has all these features as -available as far back as 3.23. While it may be possible to use 3.23 or 4.0, -you'll probably have less trouble if you use 4.1 or 5.0. +Version support +--------------- -MySQL 4.1 ---------- - -`MySQL 4.1`_ has greatly improved support for character sets. It is possible to -set different default character sets on the database, table, and column. -Previous versions have only a server-wide character set setting. It's also the -first version where the character set can be changed on the fly. 4.1 also has -support for views, but Django currently doesn't use views. - -MySQL 5.0 ---------- +Django supports MySQL 5.0.3 and higher. `MySQL 5.0`_ adds the ``information_schema`` database, which contains detailed -data on all database schema. Django's ``inspectdb`` feature uses this -``information_schema`` if it's available. 5.0 also has support for stored -procedures, but Django currently doesn't use stored procedures. +data on all database schema. Django's ``inspectdb`` feature uses this feature. + +.. versionchanged:: 1.5 + The minimum version requirement of MySQL 5.0.3 was set in Django 1.5. + +Django expects the database to support Unicode (UTF-8 encoding) and delegates to +it the task of enforcing transactions and referential integrity. It is important +to be aware of the fact that the two latter ones aren't actually enforced by +MySQL when using the MyISAM storage engine, see the next section. .. _MySQL: http://www.mysql.com/ -.. _MySQL 4.1: http://dev.mysql.com/doc/refman/4.1/en/index.html .. _MySQL 5.0: http://dev.mysql.com/doc/refman/5.0/en/index.html Storage engines @@ -381,10 +374,6 @@ for the field. This affects :class:`~django.db.models.CharField`, :class:`~django.db.models.SlugField` and :class:`~django.db.models.CommaSeparatedIntegerField`. -Furthermore, if you are using a version of MySQL prior to 5.0.3, all of those -column types have a maximum length restriction of 255 characters, regardless -of whether ``unique=True`` is specified or not. - DateTime fields ~~~~~~~~~~~~~~~