mirror of https://github.com/django/django.git
Added support for savepoints in SQLite.
Technically speaking they aren't usable yet.
This commit is contained in:
parent
e264f67174
commit
4b31a6a9e6
|
@ -100,6 +100,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
has_bulk_insert = True
|
has_bulk_insert = True
|
||||||
can_combine_inserts_with_and_without_auto_increment_pk = False
|
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
|
@cached_property
|
||||||
def supports_stddev(self):
|
def supports_stddev(self):
|
||||||
"""Confirm support for STDDEV and related stats functions
|
"""Confirm support for STDDEV and related stats functions
|
||||||
|
@ -355,6 +359,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
if self.settings_dict['NAME'] != ":memory:":
|
if self.settings_dict['NAME'] != ":memory:":
|
||||||
BaseDatabaseWrapper.close(self)
|
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):
|
def _set_autocommit(self, autocommit):
|
||||||
if autocommit:
|
if autocommit:
|
||||||
level = None
|
level = None
|
||||||
|
|
|
@ -424,8 +424,7 @@ Savepoints
|
||||||
|
|
||||||
Both the Django ORM and MySQL (when using the InnoDB :ref:`storage engine
|
Both the Django ORM and MySQL (when using the InnoDB :ref:`storage engine
|
||||||
<mysql-storage-engines>`) support database :ref:`savepoints
|
<mysql-storage-engines>`) support database :ref:`savepoints
|
||||||
<topics-db-transactions-savepoints>`, but this feature wasn't available in
|
<topics-db-transactions-savepoints>`.
|
||||||
Django until version 1.4 when such support was added.
|
|
||||||
|
|
||||||
If you use the MyISAM storage engine please be aware of the fact that you will
|
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
|
receive database-generated errors if you try to use the :ref:`savepoint-related
|
||||||
|
|
|
@ -251,11 +251,11 @@ the transaction middleware, and only modify selected functions as needed.
|
||||||
Savepoints
|
Savepoints
|
||||||
==========
|
==========
|
||||||
|
|
||||||
A savepoint is a marker within a transaction that enables you to roll back part
|
A savepoint is a marker within a transaction that enables you to roll back
|
||||||
of a transaction, rather than the full transaction. Savepoints are available
|
part of a transaction, rather than the full transaction. Savepoints are
|
||||||
with the PostgreSQL 8, Oracle and MySQL (when using the InnoDB storage engine)
|
available with the SQLite (≥ 3.6.8), PostgreSQL, Oracle and MySQL (when using
|
||||||
backends. Other backends provide the savepoint functions, but they're empty
|
the InnoDB storage engine) backends. Other backends provide the savepoint
|
||||||
operations -- they don't actually do anything.
|
functions, but they're empty operations -- they don't actually do anything.
|
||||||
|
|
||||||
Savepoints aren't especially useful if you are using the default
|
Savepoints aren't especially useful if you are using the default
|
||||||
``autocommit`` behavior of Django. However, if you are using
|
``autocommit`` behavior of Django. However, if you are using
|
||||||
|
@ -314,6 +314,21 @@ The following example demonstrates the use of savepoints::
|
||||||
Database-specific notes
|
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
|
Transactions in MySQL
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
@ -363,11 +378,11 @@ itself.
|
||||||
Savepoint rollback
|
Savepoint rollback
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you are using PostgreSQL 8 or later, you can use :ref:`savepoints
|
You can use :ref:`savepoints <topics-db-transactions-savepoints>` to control
|
||||||
<topics-db-transactions-savepoints>` to control the extent of a rollback.
|
the extent of a rollback. Before performing a database operation that could
|
||||||
Before performing a database operation that could fail, you can set or update
|
fail, you can set or update the savepoint; that way, if the operation fails,
|
||||||
the savepoint; that way, if the operation fails, you can roll back the single
|
you can roll back the single offending operation, rather than the entire
|
||||||
offending operation, rather than the entire transaction. For example::
|
transaction. For example::
|
||||||
|
|
||||||
a.save() # Succeeds, and never undone by savepoint rollback
|
a.save() # Succeeds, and never undone by savepoint rollback
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -309,6 +309,8 @@ class TestManyToManyAddTransaction(TransactionTestCase):
|
||||||
|
|
||||||
class SavepointTest(TransactionTestCase):
|
class SavepointTest(TransactionTestCase):
|
||||||
|
|
||||||
|
@skipIf(connection.vendor == 'sqlite',
|
||||||
|
"SQLite doesn't support savepoints in managed mode")
|
||||||
@skipUnlessDBFeature('uses_savepoints')
|
@skipUnlessDBFeature('uses_savepoints')
|
||||||
def test_savepoint_commit(self):
|
def test_savepoint_commit(self):
|
||||||
@commit_manually
|
@commit_manually
|
||||||
|
@ -324,6 +326,8 @@ class SavepointTest(TransactionTestCase):
|
||||||
|
|
||||||
work()
|
work()
|
||||||
|
|
||||||
|
@skipIf(connection.vendor == 'sqlite',
|
||||||
|
"SQLite doesn't support savepoints in managed mode")
|
||||||
@skipIf(connection.vendor == 'mysql' and
|
@skipIf(connection.vendor == 'mysql' and
|
||||||
connection.features._mysql_storage_engine == 'MyISAM',
|
connection.features._mysql_storage_engine == 'MyISAM',
|
||||||
"MyISAM MySQL storage engine doesn't support savepoints")
|
"MyISAM MySQL storage engine doesn't support savepoints")
|
||||||
|
|
Loading…
Reference in New Issue