Ticket #16961: ticket_16961.2.diff

File ticket_16961.2.diff, 15.3 KB (added by Anssi Kääriäinen, 12 years ago)

Resolved problems found in comment:8

  • django/db/backends/mysql/base.py

    diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
    index faaefca..bacd998 100644
    a b except ImportError, e:  
    1515    from django.core.exceptions import ImproperlyConfigured
    1616    raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
    1717
     18from django.utils.functional import cached_property
     19
    1820# We want version (1, 2, 1, 'final', 2) or later. We can't just use
    1921# lexicographic ordering in this check because then (1, 2, 1, 'gamma')
    2022# inadvertently passes the version test.
    2123version = Database.version_info
    22 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
     24if (version < (1, 2, 1) or (version[:3] == (1, 2, 1) and
    2325        (len(version) < 5 or version[3] != 'final' or version[4] < 2))):
    2426    from django.core.exceptions import ImproperlyConfigured
    2527    raise ImproperlyConfigured("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__)
    class DatabaseFeatures(BaseDatabaseFeatures):  
    163165    supports_timezones = False
    164166    requires_explicit_null_ordering_when_grouping = True
    165167    allows_primary_key_0 = False
     168    uses_savepoints = True
    166169
    167170    def __init__(self, connection):
    168171        super(DatabaseFeatures, self).__init__(connection)
    class DatabaseOperations(BaseDatabaseOperations):  
    258261                sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
    259262            sql.append('SET FOREIGN_KEY_CHECKS = 1;')
    260263
    261             # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
    262             # to reset sequence indices
    263             sql.extend(["%s %s %s %s %s;" % \
    264                 (style.SQL_KEYWORD('ALTER'),
    265                  style.SQL_KEYWORD('TABLE'),
    266                  style.SQL_TABLE(self.quote_name(sequence['table'])),
    267                  style.SQL_KEYWORD('AUTO_INCREMENT'),
    268                  style.SQL_FIELD('= 1'),
    269                 ) for sequence in sequences])
     264            # Truncate already resets the AUTO_INCREMENT field from
     265            # MySQL version 5.0.13 onwards. Refs #16961.
     266            if self.connection.mysql_version < (5,0,13):
     267                sql.extend(
     268                    ["%s %s %s %s %s;" % \
     269                     (style.SQL_KEYWORD('ALTER'),
     270                      style.SQL_KEYWORD('TABLE'),
     271                      style.SQL_TABLE(self.quote_name(sequence['table'])),
     272                      style.SQL_KEYWORD('AUTO_INCREMENT'),
     273                      style.SQL_FIELD('= 1'),
     274                     ) for sequence in sequences])
    270275            return sql
    271276        else:
    272277            return []
    class DatabaseWrapper(BaseDatabaseWrapper):  
    387392            self.connection = Database.connect(**kwargs)
    388393            self.connection.encoders[SafeUnicode] = self.connection.encoders[unicode]
    389394            self.connection.encoders[SafeString] = self.connection.encoders[str]
    390             self.features.uses_savepoints = \
    391                 self.get_server_version() >= (5, 0, 3)
    392395            connection_created.send(sender=self.__class__, connection=self)
    393396        cursor = self.connection.cursor()
    394397        if new_connection:
    class DatabaseWrapper(BaseDatabaseWrapper):  
    405408        except Database.NotSupportedError:
    406409            pass
    407410
    408     def get_server_version(self):
     411    @cached_property
     412    def mysql_version(self):
    409413        if not self.server_version:
    410414            if not self._valid_connection():
    411                 self.cursor()
     415                self.cursor().close()
    412416            m = server_version_re.match(self.connection.get_server_info())
    413417            if not m:
    414418                raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
  • django/db/backends/mysql/introspection.py

    diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py
    index ab4eebe..bb56ae3 100644
    a b class DatabaseIntrospection(BaseDatabaseIntrospection):  
    6565        key columns in given table.
    6666        """
    6767        key_columns = []
    68         try:
    69             cursor.execute("""
    70                 SELECT column_name, referenced_table_name, referenced_column_name
    71                 FROM information_schema.key_column_usage
    72                 WHERE table_name = %s
    73                     AND table_schema = DATABASE()
    74                     AND referenced_table_name IS NOT NULL
    75                     AND referenced_column_name IS NOT NULL""", [table_name])
    76             key_columns.extend(cursor.fetchall())
    77         except (ProgrammingError, OperationalError):
    78             # Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
    79             # Go through all constraints and save the equal matches.
    80             cursor.execute("SHOW CREATE TABLE %s" % self.connection.ops.quote_name(table_name))
    81             for row in cursor.fetchall():
    82                 pos = 0
    83                 while True:
    84                     match = foreign_key_re.search(row[1], pos)
    85                     if match == None:
    86                         break
    87                     pos = match.end()
    88                     key_columns.append(match.groups())
     68        cursor.execute("""
     69            SELECT column_name, referenced_table_name, referenced_column_name
     70            FROM information_schema.key_column_usage
     71            WHERE table_name = %s
     72                AND table_schema = DATABASE()
     73                AND referenced_table_name IS NOT NULL
     74                AND referenced_column_name IS NOT NULL""", [table_name])
     75        key_columns.extend(cursor.fetchall())
    8976        return key_columns
    9077
    9178    def get_primary_key_column(self, cursor, table_name):
  • django/db/backends/mysql/validation.py

    diff --git a/django/db/backends/mysql/validation.py b/django/db/backends/mysql/validation.py
    index 663cc7d..de7474d 100644
    a b from django.db.backends import BaseDatabaseValidation  
    33class DatabaseValidation(BaseDatabaseValidation):
    44    def validate_field(self, errors, opts, f):
    55        """
    6         There are some field length restrictions for MySQL:
    7 
    8         - Prior to version 5.0.3, character fields could not exceed 255
    9           characters in length.
    10         - No character (varchar) fields can have a length exceeding 255
    11           characters if they have a unique index on them.
     6        MySQL has the following field length restriction:
     7        No character (varchar) fields can have a length exceeding 255
     8        characters if they have a unique index on them.
    129        """
    1310        from django.db import models
    14         from MySQLdb import OperationalError
    15         try:
    16             db_version = self.connection.get_server_version()
    17             text_version = '.'.join([str(n) for n in db_version[:3]])
    18         except OperationalError:
    19             db_version = None
    20             text_version = ''
    2111        varchar_fields = (models.CharField, models.CommaSeparatedIntegerField,
    2212                models.SlugField)
    23         if isinstance(f, varchar_fields) and f.max_length > 255:
    24             if db_version and db_version < (5, 0, 3):
    25                 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).'
    26             elif f.unique == True:
    27                 msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when using "unique=True".'
    28             else:
    29                 msg = None
    30 
    31             if msg:
    32                 errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__, 'version': text_version})
     13        if isinstance(f, varchar_fields) and f.max_length > 255 and f.unique:
     14            msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when using "unique=True".'
     15            errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__})
  • docs/intro/install.txt

    diff --git a/docs/intro/install.txt b/docs/intro/install.txt
    index ef04eba..95f7b27 100644
    a b you should see something like::  
    3939Set up a database
    4040-----------------
    4141
    42 If you installed Python 2.6 or later, you can skip this step for now.
    43 
    44 If not, or if you'd like to work with a "large" database engine like PostgreSQL,
    45 MySQL, or Oracle, consult the :ref:`database installation information
    46 <database-installation>`.
     42This step is only necessary if you'd like to work with a "large" database engine
     43like PostgreSQL, MySQL, or Oracle. To install such a database, consult the
     44:ref:`database installation information <database-installation>`.
    4745
    4846Remove any old versions of Django
    4947---------------------------------
  • docs/ref/databases.txt

    diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
    index 59a0c36..c271946 100644
    a b lookups that use the ``LIKE`` operator in their SQL, as is done with the  
    122122MySQL notes
    123123===========
    124124
    125 Django expects the database to support transactions, referential integrity, and
    126 Unicode (UTF-8 encoding). Fortunately, MySQL_ has all these features as
    127 available as far back as 3.23. While it may be possible to use 3.23 or 4.0,
    128 you'll probably have less trouble if you use 4.1 or 5.0.
     125Version support
     126---------------
    129127
    130 MySQL 4.1
    131 ---------
     128Django supports MySQL 5.0.3 and higher.
    132129
    133 `MySQL 4.1`_ has greatly improved support for character sets. It is possible to
    134 set different default character sets on the database, table, and column.
    135 Previous versions have only a server-wide character set setting. It's also the
    136 first version where the character set can be changed on the fly. 4.1 also has
    137 support for views, but Django currently doesn't use views.
     130`MySQL 5.0`_ adds the ``information_schema`` database, which contains detailed
     131data on all database schema. Django's ``inspectdb`` feature uses this feature.
    138132
    139 MySQL 5.0
    140 ---------
     133.. versionchanged:: 1.5
     134    The minimum version requirement of MySQL 5.0.3 was set in Django 1.5.
    141135
    142 `MySQL 5.0`_ adds the ``information_schema`` database, which contains detailed
    143 data on all database schema. Django's ``inspectdb`` feature uses this
    144 ``information_schema`` if it's available. 5.0 also has support for stored
    145 procedures, but Django currently doesn't use stored procedures.
     136Django expects the database to support Unicode (UTF-8 encoding) and delegates to
     137it the task of enforcing transactions and referential integrity. It is important
     138to be aware of the fact that the two latter ones aren't actually enforced by
     139MySQL when using the MyISAM storage engine, see the next section.
    146140
    147141.. _MySQL: http://www.mysql.com/
    148 .. _MySQL 4.1: http://dev.mysql.com/doc/refman/4.1/en/index.html
    149142.. _MySQL 5.0: http://dev.mysql.com/doc/refman/5.0/en/index.html
    150143
    151144Storage engines
    for the field. This affects :class:`~django.db.models.CharField`,  
    381374:class:`~django.db.models.SlugField` and
    382375:class:`~django.db.models.CommaSeparatedIntegerField`.
    383376
    384 Furthermore, if you are using a version of MySQL prior to 5.0.3, all of those
    385 column types have a maximum length restriction of 255 characters, regardless
    386 of whether ``unique=True`` is specified or not.
    387 
    388377DateTime fields
    389378~~~~~~~~~~~~~~~
    390379
  • tests/regressiontests/backends/tests.py

    diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
    index d5b1ea8..f87b9c4 100644
    a b class OracleChecks(unittest.TestCase):  
    6161    def test_client_encoding(self):
    6262        # If the backend is Oracle, test that the client encoding is set
    6363        # correctly.  This was broken under Cygwin prior to r14781.
    64         c = connection.cursor()  # Ensure the connection is initialized.
     64        connection.cursor()  # Ensure the connection is initialized.
    6565        self.assertEqual(connection.connection.encoding, "UTF-8")
    6666        self.assertEqual(connection.connection.nencoding, "UTF-8")
    6767
     68class MySQLTests(TestCase):
     69    @unittest.skipUnless(connection.vendor == 'mysql',
     70                        "Test valid only for MySQL")
     71    def test_autoincrement(self):
     72        """
     73        Check that auto_increment fields are reset correctly by sql_flush().
     74        Before MySQL version 5.0.13 TRUNCATE did not do auto_increment reset.
     75        Refs #16961.
     76        """
     77        statements = connection.ops.sql_flush(no_style(),
     78                                              tables=['test'],
     79                                              sequences=[{
     80                                                  'table': 'test',
     81                                                  'col': 'somecol',
     82                                              }])
     83        found_reset = False
     84        for sql in statements:
     85            found_reset = found_reset or 'ALTER TABLE' in sql
     86        if connection.mysql_version < (5,0,13):
     87            self.assertTrue(found_reset)
     88        else:
     89            self.assertFalse(found_reset)
     90
     91 
    6892class DateQuotingTest(TestCase):
    6993
    7094    def test_django_date_trunc(self):
  • tests/regressiontests/test_runner/models.py

    diff --git a/tests/regressiontests/test_runner/models.py b/tests/regressiontests/test_runner/models.py
    index e69de29..9a072e6 100644
    a b  
     1from django.db import models
     2
     3class Person(models.Model):
     4    first_name = models.CharField(max_length=20)
     5    last_name = models.CharField(max_length=20)
  • tests/regressiontests/test_runner/tests.py

    diff --git a/tests/regressiontests/test_runner/tests.py b/tests/regressiontests/test_runner/tests.py
    index ccb65b4..3fc8d7f 100644
    a b from optparse import make_option  
    88from django.core.exceptions import ImproperlyConfigured
    99from django.core.management import call_command
    1010from django import db
    11 from django.test import simple
     11from django.db import connection
     12from django.test import simple, TransactionTestCase
    1213from django.test.simple import DjangoTestSuiteRunner, get_tests
    1314from django.test.testcases import connections_support_transactions
    1415from django.utils import unittest
    1516from django.utils.importlib import import_module
    1617
    1718from ..admin_scripts.tests import AdminScriptTestCase
     19from .models import Person
    1820
    1921
    2022TEST_APP_OK = 'regressiontests.test_runner.valid_app.models'
    class Sqlite3InMemoryTestDbs(unittest.TestCase):  
    262264                self.assertTrue(connections_support_transactions(), msg)
    263265            finally:
    264266                db.connections = old_db_connections
     267
     268
     269class AutoIncrementResetTest(TransactionTestCase):
     270    """
     271    Here we test creating the same model two times in different test methods,
     272    and check that both times they get "1" as their PK value. That is, we test
     273    that AutoField values start from 1 for each transactional test case.
     274    """
     275    @unittest.skipIf(connection.vendor == 'oracle',
     276                     "Oracle's auto-increment fields are not reset between "
     277                     "tests")
     278    def test_autoincrement_reset1(self):
     279        p = Person.objects.create(first_name='Jack', last_name='Smith')
     280        self.assertEquals(p.pk, 1)
     281   
     282    @unittest.skipIf(connection.vendor == 'oracle',
     283                     "Oracle's auto-increment fields are not reset between "
     284                     "tests")
     285    def test_autoincrement_reset2(self):
     286        p = Person.objects.create(first_name='Jack', last_name='Smith')
     287        self.assertEquals(p.pk, 1)
Back to Top