summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/backends/__init__.py14
-rw-r--r--docs/releases/1.5.txt9
-rw-r--r--tests/regressiontests/transactions_regress/tests.py60
3 files changed, 76 insertions, 7 deletions
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index dab9b7e213..05ef7bf62a 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -109,16 +109,18 @@ class BaseDatabaseWrapper(object):
over to the surrounding block, as a commit will commit all changes, even
those from outside. (Commits are on connection level.)
"""
- self._leave_transaction_management(self.is_managed())
if self.transaction_state:
del self.transaction_state[-1]
else:
- raise TransactionManagementError("This code isn't under transaction "
- "management")
+ raise TransactionManagementError(
+ "This code isn't under transaction management")
+ # We will pass the next status (after leaving the previous state
+ # behind) to subclass hook.
+ self._leave_transaction_management(self.is_managed())
if self._dirty:
self.rollback()
- raise TransactionManagementError("Transaction managed block ended with "
- "pending COMMIT/ROLLBACK")
+ raise TransactionManagementError(
+ "Transaction managed block ended with pending COMMIT/ROLLBACK")
self._dirty = False
def validate_thread_sharing(self):
@@ -176,6 +178,8 @@ class BaseDatabaseWrapper(object):
"""
if self.transaction_state:
return self.transaction_state[-1]
+ # Note that this setting isn't documented, and is only used here, and
+ # in enter_transaction_management()
return settings.TRANSACTIONS_MANAGED
def managed(self, flag=True):
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
index 2f20f5f9f9..4544be0eac 100644
--- a/docs/releases/1.5.txt
+++ b/docs/releases/1.5.txt
@@ -168,6 +168,15 @@ number was inside the existing page range.
It does check it now and raises an :exc:`InvalidPage` exception when the number
is either too low or too high.
+Behavior of autocommit database option on PostgreSQL changed
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+PostgreSQL's autocommit option didn't work as advertised previously. It did
+work for single transaction block, but after the first block was left the
+autocommit behavior was never restored. This bug is now fixed in 1.5. While
+this is only a bug fix, it is worth checking your applications behavior if
+you are using PostgreSQL together with the autocommit option.
+
Features deprecated in 1.5
==========================
diff --git a/tests/regressiontests/transactions_regress/tests.py b/tests/regressiontests/transactions_regress/tests.py
index abd7a4ceaa..fb26138eed 100644
--- a/tests/regressiontests/transactions_regress/tests.py
+++ b/tests/regressiontests/transactions_regress/tests.py
@@ -1,11 +1,11 @@
from __future__ import absolute_import
from django.core.exceptions import ImproperlyConfigured
-from django.db import connection, transaction
+from django.db import connection, connections, transaction, DEFAULT_DB_ALIAS
from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError
from django.test import TransactionTestCase, skipUnlessDBFeature
from django.test.utils import override_settings
-from django.utils.unittest import skipIf
+from django.utils.unittest import skipIf, skipUnless
from .models import Mod, M2mA, M2mB
@@ -175,6 +175,62 @@ class TestTransactionClosing(TransactionTestCase):
self.test_failing_query_transaction_closed()
+class TestPostgresAutocommit(TransactionTestCase):
+ """
+ Tests to make sure psycopg2's autocommit mode is restored after entering
+ and leaving transaction management. Refs #16047.
+ """
+ def setUp(self):
+ from psycopg2.extensions import (ISOLATION_LEVEL_AUTOCOMMIT,
+ ISOLATION_LEVEL_READ_COMMITTED)
+ self._autocommit = ISOLATION_LEVEL_AUTOCOMMIT
+ self._read_committed = ISOLATION_LEVEL_READ_COMMITTED
+
+ # We want a clean backend with autocommit = True, so
+ # first we need to do a bit of work to have that.
+ self._old_backend = connections[DEFAULT_DB_ALIAS]
+ settings = self._old_backend.settings_dict.copy()
+ opts = settings['OPTIONS'].copy()
+ opts['autocommit'] = True
+ settings['OPTIONS'] = opts
+ new_backend = self._old_backend.__class__(settings, DEFAULT_DB_ALIAS)
+ connections[DEFAULT_DB_ALIAS] = new_backend
+
+ def test_initial_autocommit_state(self):
+ self.assertTrue(connection.features.uses_autocommit)
+ self.assertEqual(connection.isolation_level, self._autocommit)
+
+ def test_transaction_management(self):
+ transaction.enter_transaction_management()
+ transaction.managed(True)
+ self.assertEqual(connection.isolation_level, self._read_committed)
+
+ transaction.leave_transaction_management()
+ self.assertEqual(connection.isolation_level, self._autocommit)
+
+ def test_transaction_stacking(self):
+ transaction.enter_transaction_management()
+ transaction.managed(True)
+ self.assertEqual(connection.isolation_level, self._read_committed)
+
+ transaction.enter_transaction_management()
+ self.assertEqual(connection.isolation_level, self._read_committed)
+
+ transaction.leave_transaction_management()
+ self.assertEqual(connection.isolation_level, self._read_committed)
+
+ transaction.leave_transaction_management()
+ self.assertEqual(connection.isolation_level, self._autocommit)
+
+ def tearDown(self):
+ connections[DEFAULT_DB_ALIAS] = self._old_backend
+
+TestPostgresAutocommit = skipUnless(connection.vendor == 'postgresql',
+ "This test only valid for PostgreSQL")(TestPostgresAutocommit)
+TestPostgresAutoCommit = skipUnlessDBFeature('supports_transactions')(
+ TestPostgresAutocommit)
+
+
class TestManyToManyAddTransaction(TransactionTestCase):
def test_manyrelated_add_commit(self):
"Test for https://code.djangoproject.com/ticket/16818"