From 746f2a4bed52254e1034c4462033226ff565146c Mon Sep 17 00:00:00 2001 From: Shai Berger Date: Fri, 1 Aug 2014 01:36:06 +0300 Subject: [PATCH] Fixed #23061: Avoided setting a limit on a query for get with select_for_update on Oracle Thanks Michael Miller for reporting the issue. --- django/db/backends/__init__.py | 3 +++ django/db/backends/oracle/base.py | 2 ++ django/db/models/query.py | 4 +++- tests/select_for_update/tests.py | 6 ++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 5577d3b46e..aa0191b068 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -666,6 +666,9 @@ class BaseDatabaseFeatures(object): uppercases_column_names = False + # Does the backend support "select for update" queries with limit (and offset)? + supports_select_for_update_with_limit = True + def __init__(self, connection): self.connection = connection diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 47726e76af..6711626dcb 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -121,6 +121,8 @@ class DatabaseFeatures(BaseDatabaseFeatures): closed_cursor_error_class = InterfaceError bare_select_suffix = " FROM DUAL" uppercases_column_names = True + # select for update with limit can be achieved on Oracle, but not with the current backend. + supports_select_for_update_with_limit = False class DatabaseOperations(BaseDatabaseOperations): diff --git a/django/db/models/query.py b/django/db/models/query.py index 57d9f6144a..ba99307aa3 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -357,7 +357,9 @@ class QuerySet(object): clone = self.filter(*args, **kwargs) if self.query.can_filter(): clone = clone.order_by() - clone = clone[:MAX_GET_RESULTS + 1] + if (not clone.query.select_for_update or + connections[self.db].features.supports_select_for_update_with_limit): + clone = clone[:MAX_GET_RESULTS + 1] num = len(clone) if num == 1: return clone._result_cache[0] diff --git a/tests/select_for_update/tests.py b/tests/select_for_update/tests.py index 54ba0d1d27..a0bd206923 100644 --- a/tests/select_for_update/tests.py +++ b/tests/select_for_update/tests.py @@ -265,3 +265,9 @@ class SelectForUpdateTests(TransactionTestCase): self.assertEqual(router.db_for_write(Person), query.db) finally: router.routers = old_routers + + @skipUnlessDBFeature('has_select_for_update') + def test_select_for_update_with_get(self): + with transaction.atomic(): + person = Person.objects.select_for_update().get(name='Reinhardt') + self.assertEqual(person.name, 'Reinhardt')