summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2015-02-14 09:50:38 +0100
committerAymeric Augustin <aymeric.augustin@m4x.org>2015-02-14 18:52:53 +0100
commit9b7d512d5fb88844d920929b3cbb9d6c38b8b891 (patch)
treea3e5ed8ef81408361c7dcbcc6f7bc705a147230d
parentc16f9c2d2880378e3e73365d740c7cea30ed2840 (diff)
[1.7.x] Fixed #24318 -- Set the transaction isolation level with psycopg >= 2.4.2.
Backport of 76356d96 from master
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py28
-rw-r--r--docs/releases/1.7.5.txt4
-rw-r--r--tests/backends/tests.py28
3 files changed, 55 insertions, 5 deletions
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 17bc8621f9..0454f9d94c 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -93,10 +93,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def __init__(self, *args, **kwargs):
super(DatabaseWrapper, self).__init__(*args, **kwargs)
- opts = self.settings_dict["OPTIONS"]
- RC = psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED
- self.isolation_level = opts.get('isolation_level', RC)
-
self.features = DatabaseFeatures(self)
self.ops = DatabaseOperations(self)
self.client = DatabaseClient(self)
@@ -131,7 +127,29 @@ class DatabaseWrapper(BaseDatabaseWrapper):
return conn_params
def get_new_connection(self, conn_params):
- return Database.connect(**conn_params)
+ connection = Database.connect(**conn_params)
+
+ # self.isolation_level must be set:
+ # - after connecting to the database in order to obtain the database's
+ # default when no value is explicitly specified in options.
+ # - before calling _set_autocommit() because if autocommit is on, that
+ # will set connection.isolation_level to ISOLATION_LEVEL_AUTOCOMMIT;
+ # and if autocommit is off, on psycopg2 < 2.4.2, _set_autocommit()
+ # needs self.isolation_level.
+ options = self.settings_dict['OPTIONS']
+ try:
+ self.isolation_level = options['isolation_level']
+ except KeyError:
+ self.isolation_level = connection.isolation_level
+ else:
+ # Set the isolation level to the value from OPTIONS. This isn't
+ # needed on psycopg2 < 2.4.2 because it happens as a side-effect
+ # of _set_autocommit(False).
+ if (self.isolation_level != connection.isolation_level and
+ self.psycopg2_version >= (2, 4, 2)):
+ connection.set_session(isolation_level=self.isolation_level)
+
+ return connection
def init_connection_state(self):
settings_dict = self.settings_dict
diff --git a/docs/releases/1.7.5.txt b/docs/releases/1.7.5.txt
index 604cf9146a..432031ef38 100644
--- a/docs/releases/1.7.5.txt
+++ b/docs/releases/1.7.5.txt
@@ -19,3 +19,7 @@ Bugfixes
* Fixed crash in ``contrib.sites`` migrations when a default database isn't
used (:ticket:`24332`).
+
+* Added the ability to set the isolation level on PostgreSQL with psycopg2 ≥
+ 2.4.2 (:ticket:`24318`). It was advertised as a new feature in Django 1.6
+ but it didn't work in practice.
diff --git a/tests/backends/tests.py b/tests/backends/tests.py
index 324f5da1ae..d0524378ac 100644
--- a/tests/backends/tests.py
+++ b/tests/backends/tests.py
@@ -233,6 +233,34 @@ class PostgreSQLTests(TestCase):
finally:
new_connection.close()
+ def test_connect_isolation_level(self):
+ """
+ Regression test for #18130 and #24318.
+ """
+ from psycopg2.extensions import (
+ ISOLATION_LEVEL_READ_COMMITTED as read_committed,
+ ISOLATION_LEVEL_SERIALIZABLE as serializable,
+ )
+
+ # Since this is a django.test.TestCase, a transaction is in progress
+ # and the isolation level isn't reported as 0. This test assumes that
+ # PostgreSQL is configured with the default isolation level.
+
+ # Check the level on the psycopg2 connection, not the Django wrapper.
+ self.assertEqual(connection.connection.isolation_level, read_committed)
+
+ databases = copy.deepcopy(settings.DATABASES)
+ databases[DEFAULT_DB_ALIAS]['OPTIONS']['isolation_level'] = serializable
+ new_connections = ConnectionHandler(databases)
+ new_connection = new_connections[DEFAULT_DB_ALIAS]
+ try:
+ # Start a transaction so the isolation level isn't reported as 0.
+ new_connection.set_autocommit(False)
+ # Check the level on the psycopg2 connection, not the Django wrapper.
+ self.assertEqual(new_connection.connection.isolation_level, serializable)
+ finally:
+ new_connection.close()
+
def _select(self, val):
with connection.cursor() as cursor:
cursor.execute("SELECT %s", (val,))