Ticket #15507: 15507.2.diff

File 15507.2.diff, 6.7 KB (added by ramiro, 4 years ago)

New patch, updated to apply and with skipping of a test when MySQL+MyISAM is in use

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

    diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
    a b  
    150150    requires_explicit_null_ordering_when_grouping = True
    151151    allows_primary_key_0 = False
    152152
     153    def __init__(self, connection):
     154        super(DatabaseFeatures, self).__init__(connection)
     155        self._storage_engine = None
     156
     157    def _mysql_storage_engine(self):
     158        "Internal method used in Django tests, don't rely on this from your code"
     159        if self._storage_engine is None:
     160            cursor = self.connection.cursor()
     161            cursor.execute('CREATE TABLE INTROSPECT_TEST (X INT)')
     162            # This command is MySQL specific; the second column
     163            # will tell you the default table type of the created
     164            # table. Since all Django's test tables will have the same
     165            # table type, that's enough to evaluate the feature.
     166            cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
     167            result = cursor.fetchone()
     168            cursor.execute('DROP TABLE INTROSPECT_TEST')
     169            self._storage_engine = result[1]
     170        return self._storage_engine
     171
    153172    def _can_introspect_foreign_keys(self):
    154173        "Confirm support for introspected foreign keys"
    155         cursor = self.connection.cursor()
    156         cursor.execute('CREATE TABLE INTROSPECT_TEST (X INT)')
    157         # This command is MySQL specific; the second column
    158         # will tell you the default table type of the created
    159         # table. Since all Django's test tables will have the same
    160         # table type, that's enough to evaluate the feature.
    161         cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
    162         result = cursor.fetchone()
    163         cursor.execute('DROP TABLE INTROSPECT_TEST')
    164         return result[1] != 'MyISAM'
     174        return self._mysql_storage_engine() != 'MyISAM'
    165175
    166176class DatabaseOperations(BaseDatabaseOperations):
    167177    compiler_module = "django.db.backends.mysql.compiler"
     
    285295        items_sql = "(%s)" % ", ".join(["%s"] * len(fields))
    286296        return "VALUES " + ", ".join([items_sql] * num_values)
    287297
     298    def savepoint_create_sql(self, sid):
     299        return "SAVEPOINT %s" % sid
     300
     301    def savepoint_commit_sql(self, sid):
     302        return "RELEASE SAVEPOINT %s" % sid
     303
     304    def savepoint_rollback_sql(self, sid):
     305        return "ROLLBACK TO SAVEPOINT %s" % sid
     306
    288307class DatabaseWrapper(BaseDatabaseWrapper):
    289308    vendor = 'mysql'
    290309    operators = {
     
    354373            self.connection = Database.connect(**kwargs)
    355374            self.connection.encoders[SafeUnicode] = self.connection.encoders[unicode]
    356375            self.connection.encoders[SafeString] = self.connection.encoders[str]
     376            self.features.uses_savepoints = \
     377                self.get_server_version() >= (5, 0, 3)
    357378            connection_created.send(sender=self.__class__, connection=self)
    358379        cursor = self.connection.cursor()
    359380        if new_connection:
  • docs/releases/1.4.txt

    diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
    a b  
    553553  password reset mechanism and making it available is now much easier. For
    554554  details, see :ref:`auth_password_reset`.
    555555
     556* The MySQL database backend supports taking advantage of savepoint as featured
     557  by MySQL version 5.0.3 or newer with the InnoDB storage engine.
     558
    556559Backwards incompatible changes in 1.4
    557560=====================================
    558561
  • docs/topics/db/transactions.txt

    diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
    a b  
    225225Savepoints
    226226==========
    227227
    228 A savepoint is a marker within a transaction that enables you to roll back
    229 part of a transaction, rather than the full transaction. Savepoints are
    230 available to the PostgreSQL 8 and Oracle backends. Other backends will
    231 provide the savepoint functions, but they are empty operations - they won't
    232 actually do anything.
     228A savepoint is a marker within a transaction that enables you to roll back part
     229of a transaction, rather than the full transaction. Savepoints are available to
     230the PostgreSQL 8, Oracle and MySQL (version 5.0.3 and newer, when using the
     231InnoDB storage engine) backends. Other backends will provide the savepoint
     232functions, but they are empty operations - they won't actually do anything.
     233
     234.. versionchanged:: 1.4
     235   Savepoint support when using the MySQL backend was added in Django 1.4
    233236
    234237Savepoints aren't especially useful if you are using the default
    235238``autocommit`` behavior of Django. However, if you are using
  • tests/regressiontests/transactions_regress/tests.py

    diff --git a/tests/regressiontests/transactions_regress/tests.py b/tests/regressiontests/transactions_regress/tests.py
    a b  
    44from django.db import connection, transaction
    55from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError
    66from django.test import TransactionTestCase, skipUnlessDBFeature
     7from django.utils.unittest import skipIf
    78
    89from .models import Mod, M2mA, M2mB
    910
     
    165166        except:
    166167            self.fail("A transaction consisting of a failed operation was not closed.")
    167168
     169
    168170class TestManyToManyAddTransaction(TransactionTestCase):
    169171    def test_manyrelated_add_commit(self):
    170172        "Test for https://code.djangoproject.com/ticket/16818"
     
    178180        # that the bulk insert was not auto-committed.
    179181        transaction.rollback()
    180182        self.assertEqual(a.others.count(), 1)
     183
     184
     185class SavepointTest(TransactionTestCase):
     186
     187    @skipUnlessDBFeature('uses_savepoints')
     188    def test_savepoint_commit(self):
     189        @commit_manually
     190        def work():
     191            mod = Mod.objects.create(fld=1)
     192            pk = mod.pk
     193            sid = transaction.savepoint()
     194            mod1 = Mod.objects.filter(pk=pk).update(fld=10)
     195            transaction.savepoint_commit(sid)
     196            mod2 = Mod.objects.get(pk=pk)
     197            transaction.commit()
     198            self.assertEqual(mod2.fld, 10)
     199
     200        work()
     201
     202    @skipIf(connection.vendor == 'mysql' and \
     203            connection.features._mysql_storage_engine() == 'MyISAM',
     204            "MyISAM MySQL storage engine doesn't support savepoints")
     205    @skipUnlessDBFeature('uses_savepoints')
     206    def test_savepoint_rollback(self):
     207        @commit_manually
     208        def work():
     209            mod = Mod.objects.create(fld=1)
     210            pk = mod.pk
     211            sid = transaction.savepoint()
     212            mod1 = Mod.objects.filter(pk=pk).update(fld=20)
     213            transaction.savepoint_rollback(sid)
     214            mod2 = Mod.objects.get(pk=pk)
     215            transaction.commit()
     216            self.assertEqual(mod2.fld, 1)
     217
     218        work()
Back to Top