summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2014-03-21 18:48:57 +0100
committerTim Graham <timograham@gmail.com>2014-07-24 19:27:15 -0400
commit83098dccdf3172302cde54e11c36050c1d83f996 (patch)
treed605ddda1af05413142d854ad23af46ab344a835
parent290e389fe9f8321babc2dfa55fcb7ac4ceb9524b (diff)
[1.6.x] Fixed #23089 -- Fixed transaction handling in two management commands.
Previously, when createcachetable and flush operated on non-default databases, they weren't atomic. Also avoided transactional DDL and transactional truncates on databases that don't support them (refs #22308). Backport of 753a22a635, 0757e0f30d, and 6877a9d415 from master
-rw-r--r--django/core/management/commands/createcachetable.py2
-rw-r--r--django/core/management/commands/flush.py9
-rw-r--r--django/db/backends/__init__.py3
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py1
-rw-r--r--django/db/backends/sqlite3/base.py1
-rw-r--r--docs/releases/1.6.6.txt4
-rw-r--r--tests/cache/tests.py16
7 files changed, 26 insertions, 10 deletions
diff --git a/django/core/management/commands/createcachetable.py b/django/core/management/commands/createcachetable.py
index d7ce3e93fd..0af4aeddca 100644
--- a/django/core/management/commands/createcachetable.py
+++ b/django/core/management/commands/createcachetable.py
@@ -53,7 +53,7 @@ class Command(LabelCommand):
for i, line in enumerate(table_output):
full_statement.append(' %s%s' % (line, ',' if i < len(table_output)-1 else ''))
full_statement.append(');')
- with transaction.commit_on_success_unless_managed():
+ with transaction.atomic(using=db, savepoint=connection.features.can_rollback_ddl):
curs = connection.cursor()
try:
curs.execute("\n".join(full_statement))
diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py
index 95dd634d08..82dd246160 100644
--- a/django/core/management/commands/flush.py
+++ b/django/core/management/commands/flush.py
@@ -28,8 +28,8 @@ class Command(NoArgsCommand):
're-executed, and the initial_data fixture will be re-installed.')
def handle_noargs(self, **options):
- db = options.get('database')
- connection = connections[db]
+ database = options.get('database')
+ connection = connections[database]
verbosity = int(options.get('verbosity'))
interactive = options.get('interactive')
# The following are stealth options used by Django's internals.
@@ -63,7 +63,8 @@ Are you sure you want to do this?
if confirm == 'yes':
try:
- with transaction.commit_on_success_unless_managed():
+ with transaction.atomic(using=database,
+ savepoint=connection.features.can_rollback_ddl):
cursor = connection.cursor()
for sql in sql_list:
cursor.execute(sql)
@@ -78,7 +79,7 @@ Are you sure you want to do this?
six.reraise(CommandError, CommandError(new_msg), sys.exc_info()[2])
if not inhibit_post_syncdb:
- self.emit_post_syncdb(verbosity, interactive, db)
+ self.emit_post_syncdb(verbosity, interactive, database)
# Reinstall the initial_data fixture.
if options.get('load_initial_data'):
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 12f08a2d92..ce3b6bcc85 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -652,6 +652,9 @@ class BaseDatabaseFeatures(object):
# supported by the Python driver
supports_paramstyle_pyformat = True
+ # Can we roll back DDL in a transaction?
+ can_rollback_ddl = False
+
def __init__(self, connection):
self.connection = connection
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 9aa8b476f1..f52d7a40bd 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -49,6 +49,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_tablespaces = True
supports_transactions = True
can_distinct_on_fields = True
+ can_rollback_ddl = True
class DatabaseWrapper(BaseDatabaseWrapper):
vendor = 'postgresql'
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index a219178570..c4bd38bd23 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -102,6 +102,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
can_combine_inserts_with_and_without_auto_increment_pk = False
autocommits_when_autocommit_is_off = True
atomic_transactions = False
+ can_rollback_ddl = True
supports_paramstyle_pyformat = False
@cached_property
diff --git a/docs/releases/1.6.6.txt b/docs/releases/1.6.6.txt
index c61823bfdc..c8c7a037c0 100644
--- a/docs/releases/1.6.6.txt
+++ b/docs/releases/1.6.6.txt
@@ -22,3 +22,7 @@ Bugfixes
* Restored ``pre_delete`` signals for ``GenericRelation`` cascade deletion
(`#22998 <https://code.djangoproject.com/ticket/22998>`_).
+
+* Fixed transaction handling when specifying non-default database in
+ ``createcachetable`` and ``flush``
+ (`#23089 <https://code.djangoproject.com/ticket/23089>`_).
diff --git a/tests/cache/tests.py b/tests/cache/tests.py
index 04ef21b851..b3374ab497 100644
--- a/tests/cache/tests.py
+++ b/tests/cache/tests.py
@@ -20,7 +20,7 @@ from django.core.cache import get_cache
from django.core.cache.backends.base import (CacheKeyWarning,
InvalidCacheBackendError)
from django.core.context_processors import csrf
-from django.db import router, transaction
+from django.db import connections, router, transaction
from django.core.cache.utils import make_template_fragment_key
from django.http import (HttpResponse, HttpRequest, StreamingHttpResponse,
QueryDict)
@@ -912,9 +912,16 @@ class CreateCacheTableForDBCacheTests(TestCase):
database='default',
verbosity=0, interactive=False)
# cache table should be created on 'other'
- # one query is used to create the table and another one the index
- with self.assertNumQueries(2, using='other'):
- management.call_command('createcachetable', 'cache_table',
+ # Queries:
+ # 1: create savepoint (if transactional DDL is supported)
+ # 2: create the table
+ # 3: create the index
+ # 4: release savepoint (if transactional DDL is supported)
+ from django.db import connections
+ num = 4 if connections['other'].features.can_rollback_ddl else 2
+ with self.assertNumQueries(num, using='other'):
+ management.call_command('createcachetable',
+ 'cache_table',
database='other',
verbosity=0, interactive=False)
finally:
@@ -1958,4 +1965,3 @@ class TestMakeTemplateFragmentKey(TestCase):
key = make_template_fragment_key('spam', ['abc:def%'])
self.assertEqual(key,
'template.cache.spam.f27688177baec990cdf3fbd9d9c3f469')
-