Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
View
@@ -101,6 +101,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
can_combine_inserts_with_and_without_auto_increment_pk = False
@cached_property
+ def uses_savepoints(self):
+ return Database.sqlite_version_info >= (3, 6, 8)
+
+ @cached_property
def supports_stddev(self):
"""Confirm support for STDDEV and related stats functions
@@ -355,6 +359,12 @@ def close(self):
if self.settings_dict['NAME'] != ":memory:":
BaseDatabaseWrapper.close(self)
+ def _savepoint_allowed(self):
+ # When 'isolation_level' is None, Django doesn't provide a way to
+ # create a transaction (yet) so savepoints can't be created. When it
+ # isn't, sqlite3 commits before each savepoint -- it's a bug.
+ return False
+
def _set_autocommit(self, autocommit):
if autocommit:
level = None
3  docs/ref/databases.txt
View
@@ -424,8 +424,7 @@ Savepoints
Both the Django ORM and MySQL (when using the InnoDB :ref:`storage engine
<mysql-storage-engines>`) support database :ref:`savepoints
-<topics-db-transactions-savepoints>`, but this feature wasn't available in
-Django until version 1.4 when such support was added.
+<topics-db-transactions-savepoints>`.
If you use the MyISAM storage engine please be aware of the fact that you will
receive database-generated errors if you try to use the :ref:`savepoint-related
35 docs/topics/db/transactions.txt
View
@@ -251,11 +251,11 @@ the transaction middleware, and only modify selected functions as needed.
Savepoints
==========
-A savepoint is a marker within a transaction that enables you to roll back part
-of a transaction, rather than the full transaction. Savepoints are available
-with the PostgreSQL 8, Oracle and MySQL (when using the InnoDB storage engine)
-backends. Other backends provide the savepoint functions, but they're empty
-operations -- they don't actually do anything.
+A savepoint is a marker within a transaction that enables you to roll back
+part of a transaction, rather than the full transaction. Savepoints are
+available with the SQLite (≥ 3.6.8), PostgreSQL, Oracle and MySQL (when using
+the InnoDB storage engine) backends. Other backends provide the savepoint
+functions, but they're empty operations -- they don't actually do anything.
Savepoints aren't especially useful if you are using the default
``autocommit`` behavior of Django. However, if you are using
@@ -314,6 +314,21 @@ The following example demonstrates the use of savepoints::
Database-specific notes
=======================
+Savepoints in SQLite
+--------------------
+
+While SQLite ≥ 3.6.8 supports savepoints, a flaw in the design of the
+:mod:`sqlite3` makes them hardly usable.
+
+When autocommit is enabled, savepoints don't make sense. When it's disabled,
+:mod:`sqlite3` commits implicitly before savepoint-related statement. (It
+commits before any statement other than ``SELECT``, ``INSERT``, ``UPDATE``,
+``DELETE`` and ``REPLACE``.)
+
+As a consequence, savepoints are only usable if you start a transaction
+manually while in autocommit mode, and Django doesn't provide an API to
+achieve that.
+
Transactions in MySQL
---------------------
@@ -363,11 +378,11 @@ itself.
Savepoint rollback
~~~~~~~~~~~~~~~~~~
-If you are using PostgreSQL 8 or later, you can use :ref:`savepoints
-<topics-db-transactions-savepoints>` to control the extent of a rollback.
-Before performing a database operation that could fail, you can set or update
-the savepoint; that way, if the operation fails, you can roll back the single
-offending operation, rather than the entire transaction. For example::
+You can use :ref:`savepoints <topics-db-transactions-savepoints>` to control
+the extent of a rollback. Before performing a database operation that could
+fail, you can set or update the savepoint; that way, if the operation fails,
+you can roll back the single offending operation, rather than the entire
+transaction. For example::
a.save() # Succeeds, and never undone by savepoint rollback
try:
4 tests/transactions_regress/tests.py
View
@@ -309,6 +309,8 @@ def test_manyrelated_add_commit(self):
class SavepointTest(TransactionTestCase):
+ @skipIf(connection.vendor == 'sqlite',
+ "SQLite doesn't support savepoints in managed mode")
@skipUnlessDBFeature('uses_savepoints')
def test_savepoint_commit(self):
@commit_manually
@@ -324,6 +326,8 @@ def work():
work()
+ @skipIf(connection.vendor == 'sqlite',
+ "SQLite doesn't support savepoints in managed mode")
@skipIf(connection.vendor == 'mysql' and
connection.features._mysql_storage_engine == 'MyISAM',
"MyISAM MySQL storage engine doesn't support savepoints")
Please sign in to comment.
Something went wrong with that request. Please try again.