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

View File

@ -1,6 +1,7 @@
import hashlib import hashlib
import sys import sys
import time import time
import warnings
from django.conf import settings from django.conf import settings
from django.db.utils import load_backend 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 anymore by Django code. Kept for compatibility with user code that
might use it. 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): def _prepare_for_test_db_ddl(self):
""" """

View File

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

View File

@ -445,6 +445,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
except Database.NotSupportedError: except Database.NotSupportedError:
pass pass
def _set_autocommit(self, autocommit):
self.connection.autocommit(autocommit)
def disable_constraint_checking(self): def disable_constraint_checking(self):
""" """
Disables foreign key checks, primarily for use in adding rows with forward references. Always returns True, 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): def _savepoint_commit(self, sid):
pass pass
def _set_autocommit(self, autocommit):
self.connection.autocommit = autocommit
def check_constraints(self, table_names=None): def check_constraints(self, table_names=None):
""" """
To check constraints, we set constraints to immediate. Then, when, we're done we must ensure they 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'], settings_dict['NAME'],
self._test_database_user(), 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.isolation_level = level
self.features.uses_savepoints = bool(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): def set_dirty(self):
if ((self.transaction_state and self.transaction_state[-1]) or if ((self.transaction_state and self.transaction_state[-1]) or
not self.features.uses_autocommit): not self.features.uses_autocommit):

View File

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

View File

@ -355,6 +355,17 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if self.settings_dict['NAME'] != ":memory:": if self.settings_dict['NAME'] != ":memory:":
BaseDatabaseWrapper.close(self) 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): def check_constraints(self, table_names=None):
""" """
Checks each table name in `table_names` for rows with invalid foreign key references. This method is 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 # Remove the SQLite database file
os.remove(test_database_name) os.remove(test_database_name)
def set_autocommit(self):
self.connection.connection.isolation_level = None
def test_db_signature(self): def test_db_signature(self):
""" """
Returns a tuple that uniquely identifies a test database. Returns a tuple that uniquely identifies a test database.

View File

@ -39,6 +39,18 @@ def get_connection(using=None):
using = DEFAULT_DB_ALIAS using = DEFAULT_DB_ALIAS
return connections[using] 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): def abort(using=None):
""" """
Roll back any ongoing transactions and clean the transaction management Roll back any ongoing transactions and clean the transaction management

View File

@ -63,6 +63,7 @@ def to_list(value):
value = [value] value = [value]
return value return value
real_set_autocommit = transaction.set_autocommit
real_commit = transaction.commit real_commit = transaction.commit
real_rollback = transaction.rollback real_rollback = transaction.rollback
real_enter_transaction_management = transaction.enter_transaction_management real_enter_transaction_management = transaction.enter_transaction_management
@ -73,6 +74,7 @@ def nop(*args, **kwargs):
return return
def disable_transaction_methods(): def disable_transaction_methods():
transaction.set_autocommit = nop
transaction.commit = nop transaction.commit = nop
transaction.rollback = nop transaction.rollback = nop
transaction.enter_transaction_management = nop transaction.enter_transaction_management = nop
@ -80,6 +82,7 @@ def disable_transaction_methods():
transaction.abort = nop transaction.abort = nop
def restore_transaction_methods(): def restore_transaction_methods():
transaction.set_autocommit = real_set_autocommit
transaction.commit = real_commit transaction.commit = real_commit
transaction.rollback = real_rollback transaction.rollback = real_rollback
transaction.enter_transaction_management = real_enter_transaction_management 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 * Remove the backward compatible shims introduced to rename the attributes
``ChangeList.root_query_set`` and ``ChangeList.query_set``. ``ChangeList.root_query_set`` and ``ChangeList.query_set``.
* The private API ``django.db.close_connection`` will be removed. * The following private APIs will be removed:
- ``django.db.close_connection()``
* The private API ``django.transaction.managed`` will be removed. - ``django.db.backends.creation.BaseDatabaseCreation.set_autocommit()``
- ``django.db.transaction.managed()``
2.0 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 if your transaction only reads from the database, the transaction must
be committed or rolled back before you complete a request. 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: .. _deactivate-transaction-management:
How to globally deactivate transaction management How to globally deactivate transaction management