Ticket #2705: for_update_11366.diff
File for_update_11366.diff, 12.7 KB (added by , 15 years ago) |
---|
-
django/db/models/sql/query.py
13 13 from django.utils.datastructures import SortedDict 14 14 from django.utils.encoding import force_unicode 15 15 from django.db.backends.util import truncate_name 16 from django.db import connection 16 from django.db import connection, DatabaseError 17 17 from django.db.models import signals 18 18 from django.db.models.fields import FieldDoesNotExist 19 19 from django.db.models.query_utils import select_related_descend … … 29 29 except NameError: 30 30 from sets import Set as set # Python 2.3 fallback 31 31 32 __all__ = ['Query', 'BaseQuery' ]32 __all__ = ['Query', 'BaseQuery', 'LockNotAvailable'] 33 33 34 class LockNotAvailable(DatabaseError): 35 ''' 36 Raised when a query fails because a lock was not available. 37 ''' 38 pass 39 34 40 class BaseQuery(object): 35 41 """ 36 42 A single SQL query. … … 74 80 self.order_by = [] 75 81 self.low_mark, self.high_mark = 0, None # Used for offset/limit 76 82 self.distinct = False 83 self.select_for_update = False 84 self.select_for_update_nowait = False 77 85 self.select_related = False 78 86 self.related_select_cols = [] 79 87 … … 211 219 obj.order_by = self.order_by[:] 212 220 obj.low_mark, obj.high_mark = self.low_mark, self.high_mark 213 221 obj.distinct = self.distinct 222 obj.select_for_update = self.select_for_update 223 obj.select_for_update_nowait = self.select_for_update_nowait 214 224 obj.select_related = self.select_related 215 225 obj.related_select_cols = [] 216 226 obj.aggregates = deepcopy(self.aggregates) … … 341 351 342 352 query.clear_ordering(True) 343 353 query.clear_limits() 354 query.select_for_update = False 344 355 query.select_related = False 345 356 query.related_select_cols = [] 346 357 query.related_select_fields = [] … … 459 470 result.append('LIMIT %d' % val) 460 471 result.append('OFFSET %d' % self.low_mark) 461 472 473 if self.select_for_update and self.connection.features.has_select_for_update: 474 nowait = self.select_for_update_nowait and self.connection.features.has_select_for_update 475 result.append("%s" % self.connection.ops.for_update_sql(nowait=nowait)) 476 462 477 params.extend(self.extra_params) 463 478 return ' '.join(result), tuple(params) 464 479 … … 2366 2381 else: 2367 2382 return 2368 2383 cursor = self.connection.cursor() 2369 cursor.execute(sql, params) 2384 try: 2385 cursor.execute(sql, params) 2386 except DatabaseError, e: 2387 if self.connection.features.has_select_for_update_nowait and self.connection.ops.signals_lock_not_available(e): 2388 raise LockNotAvailable(*e.args) 2389 raise 2370 2390 2371 2391 if not result_type: 2372 2392 return cursor -
django/db/models/base.py
566 566 self._collect_sub_objects(seen_objs) 567 567 568 568 # Actually delete the objects. 569 print seen_objs.items() 570 if seen_objs.items() == [(type(self), { self.pk : self })]: 571 delete_objects(seen_objs) 572 else: 573 raise Exception("related objects found") 574 # Actually delete the objects. 569 575 delete_objects(seen_objs) 570 576 571 577 delete.alters_data = True -
django/db/models/manager.py
152 152 def order_by(self, *args, **kwargs): 153 153 return self.get_query_set().order_by(*args, **kwargs) 154 154 155 def select_for_update(self, *args, **kwargs): 156 return self.get_query_set().select_for_update(*args, **kwargs) 157 155 158 def select_related(self, *args, **kwargs): 156 159 return self.get_query_set().select_related(*args, **kwargs) 157 160 -
django/db/models/__init__.py
11 11 from django.db.models.fields.subclassing import SubfieldBase 12 12 from django.db.models.fields.files import FileField, ImageField 13 13 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel 14 from django.db.models.sql.query import LockNotAvailable 14 15 from django.db.models import signals 15 16 16 17 # Admin stages. -
django/db/models/query.py
381 381 del_query = self._clone() 382 382 383 383 # Disable non-supported fields. 384 del_query.query.select_for_update = False 384 385 del_query.query.select_related = False 385 386 del_query.query.clear_ordering() 386 387 … … 533 534 else: 534 535 return self._filter_or_exclude(None, **filter_obj) 535 536 537 def select_for_update(self, **kwargs): 538 """ 539 Returns a new QuerySet instance that will select objects with a 540 FOR UPDATE lock. 541 """ 542 # Default to false for nowait 543 nowait = kwargs.pop('nowait', False) 544 obj = self._clone() 545 obj.query.select_for_update = True 546 obj.query.select_for_update_nowait = nowait 547 transaction.commit_unless_managed() 548 return obj 549 536 550 def select_related(self, *fields, **kwargs): 537 551 """ 538 552 Returns a new QuerySet instance that will select related objects. -
django/db/backends/mysql/base.py
113 113 update_can_self_select = False 114 114 allows_group_by_pk = True 115 115 related_fields_match_type = True 116 has_select_for_update = True 117 has_select_for_update_nowait = False 116 118 117 119 class DatabaseOperations(BaseDatabaseOperations): 118 120 def date_extract_sql(self, lookup_type, field_name): … … 208 210 # MySQL doesn't support microseconds 209 211 return unicode(value.replace(microsecond=0)) 210 212 213 signals_deadlock = lambda self, e: e.args[0] == ER.LOCK_DEADLOCK 214 211 215 def year_lookup_bounds(self, value): 212 216 # Again, no microseconds 213 217 first = '%s-01-01 00:00:00' -
django/db/backends/oracle/base.py
41 41 needs_datetime_string_cast = False 42 42 uses_custom_query_class = True 43 43 interprets_empty_strings_as_nulls = True 44 has_select_for_update = True 45 has_select_for_update_nowait = True 44 46 uses_savepoints = True 45 47 can_return_id_from_insert = True 46 48 … … 226 228 'column': column_name}) 227 229 return output 228 230 231 def signals_deadlock(self, exception): 232 return exception.args[0].code == 60 233 234 def signals_lock_not_available(self, exception): 235 return exception.args[0].code == 54 236 229 237 def start_transaction_sql(self): 230 238 return '' 231 239 -
django/db/backends/__init__.py
102 102 # If True, don't use integer foreign keys referring to, e.g., positive 103 103 # integer primary keys. 104 104 related_fields_match_type = False 105 has_select_for_update = False 106 has_select_for_update_nowait = False 105 107 106 108 class BaseDatabaseOperations(object): 107 109 """ … … 187 189 """ 188 190 return [] 189 191 192 def for_update_sql(self, nowait=False): 193 """ 194 Return FOR UPDATE SQL clause to lock row for update 195 """ 196 if nowait: 197 nowaitstr = ' NOWAIT' 198 else: 199 nowaitstr = '' 200 return 'FOR UPDATE' + nowaitstr 201 190 202 def fulltext_search_sql(self, field_name): 191 203 """ 192 204 Returns the SQL WHERE clause to use in order to perform a full-text -
django/db/backends/postgresql_psycopg2/base.py
17 17 try: 18 18 import psycopg2 as Database 19 19 import psycopg2.extensions 20 from psycopg2 import errorcodes 20 21 except ImportError, e: 21 22 from django.core.exceptions import ImproperlyConfigured 22 23 raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e) … … 30 31 31 32 class DatabaseFeatures(BaseDatabaseFeatures): 32 33 needs_datetime_string_cast = False 34 has_select_for_update = True 35 has_select_for_update_nowait = True 33 36 can_return_id_from_insert = False 34 37 35 38 class DatabaseOperations(PostgresqlDatabaseOperations): … … 42 45 def return_insert_id(self): 43 46 return "RETURNING %s", () 44 47 48 signals_deadlock = lambda self, e: e.pgcode == errorcodes.DEADLOCK_DETECTED 49 50 signals_lock_not_available = lambda self, e: e.pgcode == errorcodes.LOCK_NOT_AVAILABLE 51 52 45 53 class DatabaseWrapper(BaseDatabaseWrapper): 46 54 operators = { 47 55 'exact': '= %s', -
docs/ref/models/querysets.txt
875 875 Entry.objects.defer("body").only("headline", "body") 876 876 877 877 878 ``select_for_update(nowait=False)`` 879 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 880 881 Returns a queryset that will lock rows until the end of the transaction, 882 generating a SELECT ... FOR UPDATE statement on supported databases. 883 884 For example:: 885 886 entries = Entry.objects.select_for_update().filter(author=request.user) 887 888 All matched entries will be locked until the end of the transaction block, 889 meaning that other transactions will be prevented from changing or acquiring 890 locks on them. 891 892 Usually, if another transaction has already acquired a lock on one of the 893 selected rows, the query will block until the lock is released. If this is 894 not the behaviour you want, call ``select_for_update(nowait=True)``. This will 895 make the call non-blocking. If a conflicting lock is already acquired by 896 another transaction, ``django.db.models.LockNotAvailable`` will be raised when 897 the queryset is evaluated. 898 899 Using blocking locks on a database can lead to deadlocks. This occurs when two 900 concurrent transactions are both waiting on a lock the other transaction 901 already holds. To deal with deadlocks, wrap your views that use 902 ``select_for_update(nowait=False)`` with the 903 ``django.views.decorators.deadlock.handle_deadlocks`` decorator. 904 905 For example:: 906 907 from django.db import transaction 908 from django.views.decorators.deadlock import handle_deadlocks 909 910 @handle_deadlocks(max_retries=2) 911 @transaction.commit_on_success 912 def my_view(request): 913 ... 914 915 If the database engine detects a deadlock involving ``my_view`` and decides 916 to abort its transaction, it will be automatically retried. If deadlocks keep 917 occurring after two repeated attempts, 918 ``django.views.decorators.DeadlockError`` will be raised, which can be 919 propagated to the user or handled in a middleware. 920 921 Currently the ``postgresql_psycopg2``, ``oracle``, and ``mysql`` 922 database backends support ``select_for_update()`` but MySQL has no 923 support for the ``nowait`` argument. Other backends will simply 924 generate queries as if ``select_for_update()`` had not been used. 925 878 926 QuerySet methods that do not return QuerySets 879 927 --------------------------------------------- 880 928 -
docs/ref/databases.txt
325 325 column types have a maximum length restriction of 255 characters, regardless 326 326 of whether ``unique=True`` is specified or not. 327 327 328 Row locking with ``QuerySet.select_for_update()`` 329 ------------------------------------------------- 330 331 MySQL does not support the NOWAIT option to the SELECT ... FOR UPDATE 332 statement. However, you may call the ``select_for_update()`` method of a 333 queryset with ``nowait=True``. In that case, the argument will be silently 334 discarded and the generated query will block until the requested lock can be 335 acquired. 336 328 337 .. _sqlite-notes: 329 338 330 339 SQLite notes