Ticket #15507: 15507.2.diff
File 15507.2.diff, 6.7 KB (added by , 13 years ago) |
---|
-
django/db/backends/mysql/base.py
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
a b 150 150 requires_explicit_null_ordering_when_grouping = True 151 151 allows_primary_key_0 = False 152 152 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 153 172 def _can_introspect_foreign_keys(self): 154 173 "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' 165 175 166 176 class DatabaseOperations(BaseDatabaseOperations): 167 177 compiler_module = "django.db.backends.mysql.compiler" … … 285 295 items_sql = "(%s)" % ", ".join(["%s"] * len(fields)) 286 296 return "VALUES " + ", ".join([items_sql] * num_values) 287 297 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 288 307 class DatabaseWrapper(BaseDatabaseWrapper): 289 308 vendor = 'mysql' 290 309 operators = { … … 354 373 self.connection = Database.connect(**kwargs) 355 374 self.connection.encoders[SafeUnicode] = self.connection.encoders[unicode] 356 375 self.connection.encoders[SafeString] = self.connection.encoders[str] 376 self.features.uses_savepoints = \ 377 self.get_server_version() >= (5, 0, 3) 357 378 connection_created.send(sender=self.__class__, connection=self) 358 379 cursor = self.connection.cursor() 359 380 if new_connection: -
docs/releases/1.4.txt
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
a b 553 553 password reset mechanism and making it available is now much easier. For 554 554 details, see :ref:`auth_password_reset`. 555 555 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 556 559 Backwards incompatible changes in 1.4 557 560 ===================================== 558 561 -
docs/topics/db/transactions.txt
diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
a b 225 225 Savepoints 226 226 ========== 227 227 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. 228 A savepoint is a marker within a transaction that enables you to roll back part 229 of a transaction, rather than the full transaction. Savepoints are available to 230 the PostgreSQL 8, Oracle and MySQL (version 5.0.3 and newer, when using the 231 InnoDB storage engine) backends. Other backends will provide the savepoint 232 functions, 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 233 236 234 237 Savepoints aren't especially useful if you are using the default 235 238 ``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 4 4 from django.db import connection, transaction 5 5 from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError 6 6 from django.test import TransactionTestCase, skipUnlessDBFeature 7 from django.utils.unittest import skipIf 7 8 8 9 from .models import Mod, M2mA, M2mB 9 10 … … 165 166 except: 166 167 self.fail("A transaction consisting of a failed operation was not closed.") 167 168 169 168 170 class TestManyToManyAddTransaction(TransactionTestCase): 169 171 def test_manyrelated_add_commit(self): 170 172 "Test for https://code.djangoproject.com/ticket/16818" … … 178 180 # that the bulk insert was not auto-committed. 179 181 transaction.rollback() 180 182 self.assertEqual(a.others.count(), 1) 183 184 185 class 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()