Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added support for savepoints in SQLite.

Technically speaking they aren't usable yet.
  • Loading branch information...
commit 4b31a6a9e698a26e3e359e2ccf3da1505d114cf1 1 parent e264f67
Aymeric Augustin aaugustin authored
10 django/db/backends/sqlite3/base.py
@@ -101,6 +101,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
101 101 can_combine_inserts_with_and_without_auto_increment_pk = False
102 102
103 103 @cached_property
  104 + def uses_savepoints(self):
  105 + return Database.sqlite_version_info >= (3, 6, 8)
  106 +
  107 + @cached_property
104 108 def supports_stddev(self):
105 109 """Confirm support for STDDEV and related stats functions
106 110
@@ -355,6 +359,12 @@ def close(self):
355 359 if self.settings_dict['NAME'] != ":memory:":
356 360 BaseDatabaseWrapper.close(self)
357 361
  362 + def _savepoint_allowed(self):
  363 + # When 'isolation_level' is None, Django doesn't provide a way to
  364 + # create a transaction (yet) so savepoints can't be created. When it
  365 + # isn't, sqlite3 commits before each savepoint -- it's a bug.
  366 + return False
  367 +
358 368 def _set_autocommit(self, autocommit):
359 369 if autocommit:
360 370 level = None
3  docs/ref/databases.txt
@@ -424,8 +424,7 @@ Savepoints
424 424
425 425 Both the Django ORM and MySQL (when using the InnoDB :ref:`storage engine
426 426 <mysql-storage-engines>`) support database :ref:`savepoints
427   -<topics-db-transactions-savepoints>`, but this feature wasn't available in
428   -Django until version 1.4 when such support was added.
  427 +<topics-db-transactions-savepoints>`.
429 428
430 429 If you use the MyISAM storage engine please be aware of the fact that you will
431 430 receive database-generated errors if you try to use the :ref:`savepoint-related
35 docs/topics/db/transactions.txt
@@ -251,11 +251,11 @@ the transaction middleware, and only modify selected functions as needed.
251 251 Savepoints
252 252 ==========
253 253
254   -A savepoint is a marker within a transaction that enables you to roll back part
255   -of a transaction, rather than the full transaction. Savepoints are available
256   -with the PostgreSQL 8, Oracle and MySQL (when using the InnoDB storage engine)
257   -backends. Other backends provide the savepoint functions, but they're empty
258   -operations -- they don't actually do anything.
  254 +A savepoint is a marker within a transaction that enables you to roll back
  255 +part of a transaction, rather than the full transaction. Savepoints are
  256 +available with the SQLite (≥ 3.6.8), PostgreSQL, Oracle and MySQL (when using
  257 +the InnoDB storage engine) backends. Other backends provide the savepoint
  258 +functions, but they're empty operations -- they don't actually do anything.
259 259
260 260 Savepoints aren't especially useful if you are using the default
261 261 ``autocommit`` behavior of Django. However, if you are using
@@ -314,6 +314,21 @@ The following example demonstrates the use of savepoints::
314 314 Database-specific notes
315 315 =======================
316 316
  317 +Savepoints in SQLite
  318 +--------------------
  319 +
  320 +While SQLite ≥ 3.6.8 supports savepoints, a flaw in the design of the
  321 +:mod:`sqlite3` makes them hardly usable.
  322 +
  323 +When autocommit is enabled, savepoints don't make sense. When it's disabled,
  324 +:mod:`sqlite3` commits implicitly before savepoint-related statement. (It
  325 +commits before any statement other than ``SELECT``, ``INSERT``, ``UPDATE``,
  326 +``DELETE`` and ``REPLACE``.)
  327 +
  328 +As a consequence, savepoints are only usable if you start a transaction
  329 +manually while in autocommit mode, and Django doesn't provide an API to
  330 +achieve that.
  331 +
317 332 Transactions in MySQL
318 333 ---------------------
319 334
@@ -363,11 +378,11 @@ itself.
363 378 Savepoint rollback
364 379 ~~~~~~~~~~~~~~~~~~
365 380
366   -If you are using PostgreSQL 8 or later, you can use :ref:`savepoints
367   -<topics-db-transactions-savepoints>` to control the extent of a rollback.
368   -Before performing a database operation that could fail, you can set or update
369   -the savepoint; that way, if the operation fails, you can roll back the single
370   -offending operation, rather than the entire transaction. For example::
  381 +You can use :ref:`savepoints <topics-db-transactions-savepoints>` to control
  382 +the extent of a rollback. Before performing a database operation that could
  383 +fail, you can set or update the savepoint; that way, if the operation fails,
  384 +you can roll back the single offending operation, rather than the entire
  385 +transaction. For example::
371 386
372 387 a.save() # Succeeds, and never undone by savepoint rollback
373 388 try:
4 tests/transactions_regress/tests.py
@@ -309,6 +309,8 @@ def test_manyrelated_add_commit(self):
309 309
310 310 class SavepointTest(TransactionTestCase):
311 311
  312 + @skipIf(connection.vendor == 'sqlite',
  313 + "SQLite doesn't support savepoints in managed mode")
312 314 @skipUnlessDBFeature('uses_savepoints')
313 315 def test_savepoint_commit(self):
314 316 @commit_manually
@@ -324,6 +326,8 @@ def work():
324 326
325 327 work()
326 328
  329 + @skipIf(connection.vendor == 'sqlite',
  330 + "SQLite doesn't support savepoints in managed mode")
327 331 @skipIf(connection.vendor == 'mysql' and
328 332 connection.features._mysql_storage_engine == 'MyISAM',
329 333 "MyISAM MySQL storage engine doesn't support savepoints")

0 comments on commit 4b31a6a

Please sign in to comment.
Something went wrong with that request. Please try again.