Added an API to control database-level autocommit.

This commit is contained in:
Aymeric Augustin 2013-03-02 13:47:46 +01:00
parent 7aacde84f2
commit f515619494
14 changed files with 81 additions and 13 deletions

View File

@ -44,6 +44,7 @@ class BaseDatabaseWrapper(object):
self.savepoint_state = 0
# Transaction management related attributes
self.autocommit = False
self.transaction_state = []
# Tracks if the connection is believed to be in transaction. This is
# set somewhat aggressively, as the DBAPI doesn't make it easy to
@ -232,6 +233,12 @@ class BaseDatabaseWrapper(object):
"""
pass
def _set_autocommit(self, autocommit):
"""
Backend-specific implementation to enable or disable autocommit.
"""
raise NotImplementedError
##### Generic transaction management methods #####
def enter_transaction_management(self, managed=True, forced=False):
@ -274,6 +281,13 @@ class BaseDatabaseWrapper(object):
raise TransactionManagementError(
"Transaction managed block ended with pending COMMIT/ROLLBACK")
def set_autocommit(self, autocommit=True):
"""
Enable or disable autocommit.
"""
self._set_autocommit(autocommit)
self.autocommit = autocommit
def abort(self):
"""
Roll back any ongoing transaction and clean the transaction state

View File

@ -1,6 +1,7 @@
import hashlib
import sys
import time
import warnings
from django.conf import settings
from django.db.utils import load_backend
@ -466,7 +467,10 @@ class BaseDatabaseCreation(object):
anymore by Django code. Kept for compatibility with user code that
might use it.
"""
pass
warnings.warn(
"set_autocommit was moved from BaseDatabaseCreation to "
"BaseDatabaseWrapper.", PendingDeprecationWarning, stacklevel=2)
return self.connection.set_autocommit()
def _prepare_for_test_db_ddl(self):
"""

View File

@ -57,6 +57,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
_savepoint_rollback = ignore
_enter_transaction_management = complain
_leave_transaction_management = ignore
_set_autocommit = complain
set_dirty = complain
set_clean = complain
commit_unless_managed = complain

View File

@ -445,6 +445,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
except Database.NotSupportedError:
pass
def _set_autocommit(self, autocommit):
self.connection.autocommit(autocommit)
def disable_constraint_checking(self):
"""
Disables foreign key checks, primarily for use in adding rows with forward references. Always returns True,

View File

@ -612,6 +612,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def _savepoint_commit(self, sid):
pass
def _set_autocommit(self, autocommit):
self.connection.autocommit = autocommit
def check_constraints(self, table_names=None):
"""
To check constraints, we set constraints to immediate. Then, when, we're done we must ensure they

View File

@ -273,6 +273,3 @@ class DatabaseCreation(BaseDatabaseCreation):
settings_dict['NAME'],
self._test_database_user(),
)
def set_autocommit(self):
self.connection.connection.autocommit = True

View File

@ -201,6 +201,14 @@ class DatabaseWrapper(BaseDatabaseWrapper):
self.isolation_level = level
self.features.uses_savepoints = bool(level)
def _set_autocommit(self, autocommit):
if autocommit:
level = psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT
else:
level = self.settings_dict["OPTIONS"].get('isolation_level',
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
self._set_isolation_level(level)
def set_dirty(self):
if ((self.transaction_state and self.transaction_state[-1]) or
not self.features.uses_autocommit):

View File

@ -78,9 +78,6 @@ class DatabaseCreation(BaseDatabaseCreation):
' text_pattern_ops'))
return output
def set_autocommit(self):
self._prepare_for_test_db_ddl()
def _prepare_for_test_db_ddl(self):
"""Rollback and close the active transaction."""
# Make sure there is an open connection.

View File

@ -355,6 +355,17 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if self.settings_dict['NAME'] != ":memory:":
BaseDatabaseWrapper.close(self)
def _set_autocommit(self, autocommit):
if autocommit:
level = None
else:
# sqlite3's internal default is ''. It's different from None.
# See Modules/_sqlite/connection.c.
level = ''
# 'isolation_level' is a misleading API.
# SQLite always runs at the SERIALIZABLE isolation level.
self.connection.isolation_level = level
def check_constraints(self, table_names=None):
"""
Checks each table name in `table_names` for rows with invalid foreign key references. This method is

View File

@ -72,9 +72,6 @@ class DatabaseCreation(BaseDatabaseCreation):
# Remove the SQLite database file
os.remove(test_database_name)
def set_autocommit(self):
self.connection.connection.isolation_level = None
def test_db_signature(self):
"""
Returns a tuple that uniquely identifies a test database.

View File

@ -39,6 +39,18 @@ def get_connection(using=None):
using = DEFAULT_DB_ALIAS
return connections[using]
def get_autocommit(using=None):
"""
Get the autocommit status of the connection.
"""
return get_connection(using).autocommit
def set_autocommit(using=None, autocommit=True):
"""
Set the autocommit status of the connection.
"""
return get_connection(using).set_autocommit(autocommit)
def abort(using=None):
"""
Roll back any ongoing transactions and clean the transaction management

View File

@ -63,6 +63,7 @@ def to_list(value):
value = [value]
return value
real_set_autocommit = transaction.set_autocommit
real_commit = transaction.commit
real_rollback = transaction.rollback
real_enter_transaction_management = transaction.enter_transaction_management
@ -73,6 +74,7 @@ def nop(*args, **kwargs):
return
def disable_transaction_methods():
transaction.set_autocommit = nop
transaction.commit = nop
transaction.rollback = nop
transaction.enter_transaction_management = nop
@ -80,6 +82,7 @@ def disable_transaction_methods():
transaction.abort = nop
def restore_transaction_methods():
transaction.set_autocommit = real_set_autocommit
transaction.commit = real_commit
transaction.rollback = real_rollback
transaction.enter_transaction_management = real_enter_transaction_management

View File

@ -348,9 +348,10 @@ these changes.
* Remove the backward compatible shims introduced to rename the attributes
``ChangeList.root_query_set`` and ``ChangeList.query_set``.
* The private API ``django.db.close_connection`` will be removed.
* The private API ``django.transaction.managed`` will be removed.
* The following private APIs will be removed:
- ``django.db.close_connection()``
- ``django.db.backends.creation.BaseDatabaseCreation.set_autocommit()``
- ``django.db.transaction.managed()``
2.0
---

View File

@ -208,6 +208,23 @@ This applies to all database operations, not just write operations. Even
if your transaction only reads from the database, the transaction must
be committed or rolled back before you complete a request.
.. _managing-autocommit:
Managing autocommit
===================
.. versionadded:: 1.6
Django provides a straightforward API to manage the autocommit state of each
database connection, if you need to.
.. function:: get_autocommit(using=None)
.. function:: set_autocommit(using=None, autocommit=True)
These functions take a ``using`` argument which should be the name of a
database. If it isn't provided, Django uses the ``"default"`` database.
.. _deactivate-transaction-management:
How to globally deactivate transaction management