diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 8917fc3b23..063c0ae411 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -51,6 +51,7 @@ class BaseDatabaseFeatures(object): uses_case_insensitive_names = False uses_custom_query_class = False empty_fetchmany_value = [] + update_can_self_select = True class BaseDatabaseOperations(object): """ diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 17aa6f13bf..da57ecbe47 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -63,6 +63,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): autoindexes_primary_keys = False inline_fk_references = False empty_fetchmany_value = () + update_can_self_select = False class DatabaseOperations(BaseDatabaseOperations): def date_extract_sql(self, lookup_type, field_name): diff --git a/django/db/backends/mysql_old/base.py b/django/db/backends/mysql_old/base.py index efbfeeafc5..0ae114d176 100644 --- a/django/db/backends/mysql_old/base.py +++ b/django/db/backends/mysql_old/base.py @@ -67,6 +67,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): autoindexes_primary_keys = False inline_fk_references = False empty_fetchmany_value = () + update_can_self_select = False class DatabaseOperations(BaseDatabaseOperations): def date_extract_sql(self, lookup_type, field_name): diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py index 1f9a13ecdc..7385cd00e1 100644 --- a/django/db/models/sql/subqueries.py +++ b/django/db/models/sql/subqueries.py @@ -159,20 +159,37 @@ class UpdateQuery(Query): # from other tables. query = self.clone(klass=Query) query.bump_prefix() - query.select = [] query.extra_select = {} - query.add_fields([query.model._meta.pk.name]) + first_table = query.tables[0] + if query.alias_refcount[first_table] == 1: + # We can remove one table from the inner query. + query.unref_alias(first_table) + for i in xrange(1, len(query.tables)): + table = query.tables[i] + if query.alias_refcount[table]: + break + join_info = query.alias_map[table] + query.select = [(join_info[RHS_ALIAS], join_info[RHS_JOIN_COL])] + must_pre_select = False + else: + query.select = [] + query.add_fields([query.model._meta.pk.name]) + must_pre_select = not self.connection.features.update_can_self_select # Now we adjust the current query: reset the where clause and get rid # of all the tables we don't need (since they're in the sub-select). self.where = self.where_class() - if self.related_updates: + if self.related_updates or must_pre_select: + # Either we're using the idents in multiple update queries (so + # don't want them to change), or the db backend doesn't support + # selecting from the updating table (e.g. MySQL). idents = [] for rows in query.execute_sql(MULTI): idents.extend([r[0] for r in rows]) self.add_filter(('pk__in', idents)) self.related_ids = idents else: + # The fast path. Filters and updates in one query. self.add_filter(('pk__in', query)) for alias in self.tables[1:]: self.alias_refcount[alias] = 0