Commit af9e9386 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Enabled autocommit for PostgreSQL.

For users who didn't activate autocommit in their database options, this
is backwards-incompatible in "non-managed" aka "auto" transaction state.
This state now uses database-level autocommit instead of ORM-level
autocommit.

Also removed the uses_autocommit feature which lost its purpose.
parent 8717b066
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -479,7 +479,6 @@ class BaseDatabaseFeatures(object):
    can_use_chunked_reads = True
    can_return_id_from_insert = False
    has_bulk_insert = False
    uses_autocommit = False
    uses_savepoints = False
    can_combine_inserts_with_and_without_auto_increment_pk = False

+5 −9
Original line number Diff line number Diff line
@@ -88,9 +88,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
        self.introspection = DatabaseIntrospection(self)
        self.validation = BaseDatabaseValidation(self)

        autocommit = opts.get('autocommit', False)
        self.features.uses_autocommit = autocommit
        self.features.uses_savepoints = not autocommit
        self.features.uses_savepoints = False

    def get_connection_params(self):
        settings_dict = self.settings_dict
@@ -139,8 +137,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
                self.connection.cursor().execute(
                        self.ops.set_time_zone_sql(), [tz])
        self.connection.set_isolation_level(self.isolation_level)
        if self.features.uses_autocommit:
            self.set_autocommit(True)
        self.set_autocommit(not settings.TRANSACTIONS_MANAGED)

    def create_cursor(self):
        cursor = self.connection.cursor()
@@ -175,7 +172,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
        """
        if self.connection is None:             # Force creating a connection.
            self.cursor().close()
        if self.features.uses_autocommit and managed and self.autocommit:
        if managed and self.autocommit:
            self.set_autocommit(False)
            self.features.uses_savepoints = True

@@ -186,7 +183,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
        """
        if self.connection is None:             # Force creating a connection.
            self.cursor().close()
        if self.features.uses_autocommit and not managed and not self.autocommit:
        if not managed and not self.autocommit:
            self.rollback()                     # Must terminate transaction first.
            self.set_autocommit(True)
            self.features.uses_savepoints = False
@@ -209,8 +206,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
            self.connection.set_isolation_level(level)

    def set_dirty(self):
        if ((self.transaction_state and self.transaction_state[-1]) or
                not self.features.uses_autocommit):
        if self.transaction_state and self.transaction_state[-1]:
            super(DatabaseWrapper, self).set_dirty()

    def check_constraints(self, table_names=None):
+2 −2
Original line number Diff line number Diff line
@@ -302,8 +302,8 @@ class PostgresNewConnectionTest(TestCase):
    transaction is rolled back.
    """
    @unittest.skipUnless(
        connection.vendor == 'postgresql' and connection.isolation_level > 0,
        "This test applies only to PostgreSQL without autocommit")
        connection.vendor == 'postgresql',
        "This test applies only to PostgreSQL")
    def test_connect_and_rollback(self):
        new_connections = ConnectionHandler(settings.DATABASES)
        new_connection = new_connections[DEFAULT_DB_ALIAS]
+5 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ from django.test.utils import override_settings
from django.utils import six
from django.utils.encoding import force_str
from django.utils.six.moves import xrange
from django.utils.unittest import expectedFailure

from .models import Band

@@ -698,6 +699,10 @@ class TransactionMiddlewareTest(TransactionTestCase):
        self.assertFalse(transaction.is_dirty())
        self.assertEqual(Band.objects.count(), 1)

    # TODO: update this test to account for database-level autocommit.
    # Currently it fails under PostgreSQL because connections are never
    # marked dirty in non-managed mode.
    @expectedFailure
    def test_unmanaged_response(self):
        transaction.enter_transaction_management(False)
        self.assertEqual(Band.objects.count(), 0)
+7 −8
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ from django.db import connection, connections, transaction, DEFAULT_DB_ALIAS, Da
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, skipUnless
from django.utils.unittest import skipIf, skipUnless, expectedFailure

from .models import Mod, M2mA, M2mB

@@ -173,10 +173,6 @@ class TestNewConnection(TransactionTestCase):
    def setUp(self):
        self._old_backend = connections[DEFAULT_DB_ALIAS]
        settings = self._old_backend.settings_dict.copy()
        opts = settings['OPTIONS'].copy()
        if 'autocommit' in opts:
            opts['autocommit'] = False
        settings['OPTIONS'] = opts
        new_backend = self._old_backend.__class__(settings, DEFAULT_DB_ALIAS)
        connections[DEFAULT_DB_ALIAS] = new_backend

@@ -189,6 +185,8 @@ class TestNewConnection(TransactionTestCase):
            connections[DEFAULT_DB_ALIAS].close()
            connections[DEFAULT_DB_ALIAS] = self._old_backend

    # TODO: update this test to account for database-level autocommit.
    @expectedFailure
    def test_commit(self):
        """
        Users are allowed to commit and rollback connections.
@@ -210,6 +208,8 @@ class TestNewConnection(TransactionTestCase):
        connection.leave_transaction_management()
        self.assertEqual(orig_dirty, connection._dirty)

    # TODO: update this test to account for database-level autocommit.
    @expectedFailure
    def test_commit_unless_managed(self):
        cursor = connection.cursor()
        cursor.execute("INSERT into transactions_regress_mod (fld) values (2)")
@@ -220,6 +220,8 @@ class TestNewConnection(TransactionTestCase):
        connection.commit_unless_managed()
        self.assertFalse(connection.is_dirty())

    # TODO: update this test to account for database-level autocommit.
    @expectedFailure
    def test_commit_unless_managed_in_managed(self):
        cursor = connection.cursor()
        connection.enter_transaction_management()
@@ -260,7 +262,6 @@ class TestPostgresAutocommitAndIsolation(TransactionTestCase):
        self._old_backend = connections[DEFAULT_DB_ALIAS]
        settings = self._old_backend.settings_dict.copy()
        opts = settings['OPTIONS'].copy()
        opts['autocommit'] = True
        opts['isolation_level'] = ISOLATION_LEVEL_SERIALIZABLE
        settings['OPTIONS'] = opts
        new_backend = self._old_backend.__class__(settings, DEFAULT_DB_ALIAS)
@@ -276,8 +277,6 @@ class TestPostgresAutocommitAndIsolation(TransactionTestCase):
    def test_initial_autocommit_state(self):
        # Autocommit is activated when the connection is created.
        connection.cursor().close()

        self.assertTrue(connection.features.uses_autocommit)
        self.assertTrue(connection.autocommit)

    def test_transaction_management(self):