diff options
| author | Simon Charette <charette.s@gmail.com> | 2016-06-23 11:52:14 -0400 |
|---|---|---|
| committer | Simon Charette <charette.s@gmail.com> | 2016-08-08 12:01:43 -0400 |
| commit | b8e6e1b43b0bccec178dcc6708c5c8295abfb2e5 (patch) | |
| tree | f3c146f02ecf6b0181d790cc67d83ad53cd629f9 /django | |
| parent | 46509cf13dbf049f75077981c29ef2c60b5a96ab (diff) | |
Fixed #26500 -- Added SKIP LOCKED support to select_for_update().
Thanks Tim for the review.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/backends/base/features.py | 1 | ||||
| -rw-r--r-- | django/db/backends/base/operations.py | 4 | ||||
| -rw-r--r-- | django/db/backends/oracle/features.py | 1 | ||||
| -rw-r--r-- | django/db/backends/postgresql/features.py | 5 | ||||
| -rw-r--r-- | django/db/models/query.py | 5 | ||||
| -rw-r--r-- | django/db/models/sql/compiler.py | 11 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 2 |
7 files changed, 23 insertions, 6 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 484b272294..9bb0ed6848 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -36,6 +36,7 @@ class BaseDatabaseFeatures(object): allow_sliced_subqueries = True has_select_for_update = False has_select_for_update_nowait = False + has_select_for_update_skip_locked = False supports_select_related = True diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index 4e5814e58f..1ed11b178e 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -177,12 +177,14 @@ class BaseDatabaseOperations(object): """ return [] - def for_update_sql(self, nowait=False): + def for_update_sql(self, nowait=False, skip_locked=False): """ Returns the FOR UPDATE SQL clause to lock rows for an update operation. """ if nowait: return 'FOR UPDATE NOWAIT' + elif skip_locked: + return 'FOR UPDATE SKIP LOCKED' else: return 'FOR UPDATE' diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index 84e18cf9ba..956f7a051b 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -13,6 +13,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): uses_savepoints = True has_select_for_update = True has_select_for_update_nowait = True + has_select_for_update_skip_locked = True can_return_id_from_insert = True allow_sliced_subqueries = False supports_subqueries_in_group_by = False diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index 218a7645de..a9e1c77480 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -1,5 +1,6 @@ from django.db.backends.base.features import BaseDatabaseFeatures from django.db.utils import InterfaceError +from django.utils.functional import cached_property class DatabaseFeatures(BaseDatabaseFeatures): @@ -31,3 +32,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): greatest_least_ignores_nulls = True can_clone_databases = True supports_temporal_subtraction = True + + @cached_property + def has_select_for_update_skip_locked(self): + return self.connection.pg_version >= 90500 diff --git a/django/db/models/query.py b/django/db/models/query.py index 07f1fc414f..0a0965fe47 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -835,15 +835,18 @@ class QuerySet(object): else: return self._filter_or_exclude(None, **filter_obj) - def select_for_update(self, nowait=False): + def select_for_update(self, nowait=False, skip_locked=False): """ Returns a new QuerySet instance that will select objects with a FOR UPDATE lock. """ + if nowait and skip_locked: + raise ValueError('The nowait option cannot be used with skip_locked.') obj = self._clone() obj._for_write = True obj.query.select_for_update = True obj.query.select_for_update_nowait = nowait + obj.query.select_for_update_skip_locked = skip_locked return obj def select_related(self, *fields): diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index cedb140143..5eaac1fc74 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -445,13 +445,16 @@ class SQLCompiler(object): "select_for_update cannot be used outside of a transaction." ) - # If we've been asked for a NOWAIT query but the backend does - # not support it, raise a DatabaseError otherwise we could get - # an unexpected deadlock. nowait = self.query.select_for_update_nowait + skip_locked = self.query.select_for_update_skip_locked + # If we've been asked for a NOWAIT/SKIP LOCKED query but the + # backend does not support it, raise a DatabaseError otherwise + # we could get an unexpected deadlock. if nowait and not self.connection.features.has_select_for_update_nowait: raise DatabaseError('NOWAIT is not supported on this database backend.') - result.append(self.connection.ops.for_update_sql(nowait=nowait)) + elif skip_locked and not self.connection.features.has_select_for_update_skip_locked: + raise DatabaseError('SKIP LOCKED is not supported on this database backend.') + result.append(self.connection.ops.for_update_sql(nowait=nowait, skip_locked=skip_locked)) return ' '.join(result), tuple(params) finally: diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index d182242ce4..426087f8af 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -167,6 +167,7 @@ class Query(object): self.distinct_fields = [] self.select_for_update = False self.select_for_update_nowait = False + self.select_for_update_skip_locked = False self.select_related = False # Arbitrary limit for select_related to prevents infinite recursion. @@ -286,6 +287,7 @@ class Query(object): obj.distinct_fields = self.distinct_fields[:] obj.select_for_update = self.select_for_update obj.select_for_update_nowait = self.select_for_update_nowait + obj.select_for_update_skip_locked = self.select_for_update_skip_locked obj.select_related = self.select_related obj.values_select = self.values_select[:] obj._annotations = self._annotations.copy() if self._annotations is not None else None |
