Ticket #2705: for_update_1.2.0-final.patch.diff
File for_update_1.2.0-final.patch.diff, 12.2 KB (added by , 15 years ago) |
---|
-
django/db/models/sql/compiler.py
114 114 result.append('LIMIT %d' % val) 115 115 result.append('OFFSET %d' % self.query.low_mark) 116 116 117 if self.query.select_for_update and self.connection.features.has_select_for_update: 118 nowait = self.query.select_for_update_nowait and self.connection.features.has_select_for_update 119 result.append("%s" % self.connection.ops.for_update_sql(nowait=nowait)) 120 117 121 return ' '.join(result), tuple(params) 118 122 119 123 def as_nested_sql(self): -
django/db/models/sql/query.py
11 11 from django.utils.tree import Node 12 12 from django.utils.datastructures import SortedDict 13 13 from django.utils.encoding import force_unicode 14 from django.db import connections, DEFAULT_DB_ALIAS 14 from django.db import connections, DEFAULT_DB_ALIAS, DatabaseError 15 15 from django.db.models import signals 16 16 from django.db.models.fields import FieldDoesNotExist 17 17 from django.db.models.query_utils import select_related_descend, InvalidQuery … … 23 23 ExtraWhere, AND, OR) 24 24 from django.core.exceptions import FieldError 25 25 26 __all__ = ['Query', 'RawQuery' ]26 __all__ = ['Query', 'RawQuery', 'LockNotAvailable'] 27 27 28 29 class LockNotAvailable(DatabaseError): 30 ''' 31 Raised when a query fails because a lock was not available. 32 ''' 33 pass 34 35 28 36 class RawQuery(object): 29 37 """ 30 38 A single raw SQL query … … 84 92 85 93 def _execute_query(self): 86 94 self.cursor = connections[self.using].cursor() 87 self.cursor.execute(self.sql, self.params) 95 try: 96 self.cursor.execute(self.sql, self.params) 97 except DatabaseError, e: 98 if self.connection.features.has_select_for_update_nowait and self.connection.ops.signals_lock_not_available(e): 99 raise LockNotAvailable(*e.args) 100 raise 88 101 89 90 102 class Query(object): 91 103 """ 92 104 A single SQL query. … … 131 143 self.order_by = [] 132 144 self.low_mark, self.high_mark = 0, None # Used for offset/limit 133 145 self.distinct = False 146 self.select_for_update = False 147 self.select_for_update_nowait = False 134 148 self.select_related = False 135 149 self.related_select_cols = [] 136 150 … … 259 273 obj.order_by = self.order_by[:] 260 274 obj.low_mark, obj.high_mark = self.low_mark, self.high_mark 261 275 obj.distinct = self.distinct 276 obj.select_for_update = self.select_for_update 277 obj.select_for_update_nowait = self.select_for_update_nowait 262 278 obj.select_related = self.select_related 263 279 obj.related_select_cols = [] 264 280 obj.aggregates = deepcopy(self.aggregates, memo=memo) … … 359 375 360 376 query.clear_ordering(True) 361 377 query.clear_limits() 378 query.select_for_update = False 362 379 query.select_related = False 363 380 query.related_select_cols = [] 364 381 query.related_select_fields = [] -
django/db/models/manager.py
164 164 def order_by(self, *args, **kwargs): 165 165 return self.get_query_set().order_by(*args, **kwargs) 166 166 167 def select_for_update(self, *args, **kwargs): 168 return self.get_query_set().select_for_update(*args, **kwargs) 169 167 170 def select_related(self, *args, **kwargs): 168 171 return self.get_query_set().select_related(*args, **kwargs) 169 172 -
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
424 424 del_query._for_write = True 425 425 426 426 # Disable non-supported fields. 427 del_query.query.select_for_update = False 427 428 del_query.query.select_related = False 428 429 del_query.query.clear_ordering() 429 430 … … 585 586 else: 586 587 return self._filter_or_exclude(None, **filter_obj) 587 588 589 def select_for_update(self, **kwargs): 590 """ 591 Returns a new QuerySet instance that will select objects with a 592 FOR UPDATE lock. 593 """ 594 # Default to false for nowait 595 nowait = kwargs.pop('nowait', False) 596 obj = self._clone() 597 obj.query.select_for_update = True 598 obj.query.select_for_update_nowait = nowait 599 return obj 600 588 601 def select_related(self, *fields, **kwargs): 589 602 """ 590 603 Returns a new QuerySet instance that will select related objects. -
django/db/backends/mysql/base.py
124 124 allows_group_by_pk = True 125 125 related_fields_match_type = True 126 126 allow_sliced_subqueries = False 127 has_select_for_update = True 128 has_select_for_update_nowait = False 127 129 128 130 class DatabaseOperations(BaseDatabaseOperations): 129 131 compiler_module = "django.db.backends.mysql.compiler" 132 signals_deadlock = lambda self, e: e.args[0] == ER.LOCK_DEADLOCK 130 133 131 134 def date_extract_sql(self, lookup_type, field_name): 132 135 # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html -
django/db/backends/oracle/base.py
50 50 uses_savepoints = True 51 51 can_return_id_from_insert = True 52 52 allow_sliced_subqueries = False 53 has_select_for_update = True 54 has_select_for_update_nowait = True 53 55 54 56 55 57 class DatabaseOperations(BaseDatabaseOperations): … … 279 281 'column': column_name}) 280 282 return output 281 283 284 def signals_deadlock(self, exception): 285 return exception.args[0].code == 60 286 287 def signals_lock_not_available(self, exception): 288 return exception.args[0].code == 54 289 282 290 def start_transaction_sql(self): 283 291 return '' 284 292 -
django/db/backends/__init__.py
96 96 # integer primary keys. 97 97 related_fields_match_type = False 98 98 allow_sliced_subqueries = True 99 has_select_for_update = False 100 has_select_for_update_nowait = False 99 101 100 102 class BaseDatabaseOperations(object): 101 103 """ … … 185 187 ordering. 186 188 """ 187 189 return [] 190 def for_update_sql(self, nowait=False): 191 """ 192 Return FOR UPDATE SQL clause to lock row for update 193 """ 194 if nowait: 195 nowaitstr = ' NOWAIT' 196 else: 197 nowaitstr = '' 198 return 'FOR UPDATE' + nowaitstr 188 199 189 200 def fulltext_search_sql(self, field_name): 190 201 """ -
django/db/backends/postgresql_psycopg2/base.py
19 19 try: 20 20 import psycopg2 as Database 21 21 import psycopg2.extensions 22 from psycopg2 import errorcodes 22 23 except ImportError, e: 23 24 from django.core.exceptions import ImproperlyConfigured 24 25 raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e) … … 67 68 class DatabaseFeatures(BaseDatabaseFeatures): 68 69 needs_datetime_string_cast = False 69 70 can_return_id_from_insert = False 71 has_select_for_update = True 72 has_select_for_update_nowait = True 70 73 71 74 class DatabaseOperations(PostgresqlDatabaseOperations): 75 signals_deadlock = lambda self, e: e.pgcode == errorcodes.DEADLOCK_DETECTED 76 signals_lock_not_available = lambda self, e: e.pgcode == errorcodes.LOCK_NOT_AVAILABLE 72 77 def last_executed_query(self, cursor, sql, params): 73 78 # With psycopg2, cursor objects have a "query" attribute that is the 74 79 # exact query sent to the database. See docs here: -
docs/ref/models/querysets.txt
963 963 # queries the database with the 'backup' alias 964 964 >>> Entry.objects.using('backup') 965 965 966 ``select_for_update(nowait=False)`` 967 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 968 969 Returns a queryset that will lock rows until the end of the transaction, 970 generating a SELECT ... FOR UPDATE statement on supported databases. 971 972 For example:: 973 974 entries = Entry.objects.select_for_update().filter(author=request.user) 975 976 All matched entries will be locked until the end of the transaction block, 977 meaning that other transactions will be prevented from changing or acquiring 978 locks on them. 979 980 Usually, if another transaction has already acquired a lock on one of the 981 selected rows, the query will block until the lock is released. If this is 982 not the behaviour you want, call ``select_for_update(nowait=True)``. This will 983 make the call non-blocking. If a conflicting lock is already acquired by 984 another transaction, ``django.db.models.LockNotAvailable`` will be raised when 985 the queryset is evaluated. 986 987 Using blocking locks on a database can lead to deadlocks. This occurs when two 988 concurrent transactions are both waiting on a lock the other transaction 989 already holds. To deal with deadlocks, wrap your views that use 990 ``select_for_update(nowait=False)`` with the 991 ``django.views.decorators.deadlock.handle_deadlocks`` decorator. 992 993 For example:: 994 995 from django.db import transaction 996 from django.views.decorators.deadlock import handle_deadlocks 997 998 @handle_deadlocks(max_retries=2) 999 @transaction.commit_on_success 1000 def my_view(request): 1001 ... 1002 1003 If the database engine detects a deadlock involving ``my_view`` and decides 1004 to abort its transaction, it will be automatically retried. If deadlocks keep 1005 occurring after two repeated attempts, 1006 ``django.views.decorators.DeadlockError`` will be raised, which can be 1007 propagated to the user or handled in a middleware. 1008 1009 Currently the ``postgresql_psycopg2``, ``oracle``, and ``mysql`` 1010 database backends support ``select_for_update()`` but MySQL has no 1011 support for the ``nowait`` argument. Other backends will simply 1012 generate queries as if ``select_for_update()`` had not been used. 966 1013 967 1014 QuerySet methods that do not return QuerySets 968 1015 --------------------------------------------- -
docs/ref/databases.txt
345 345 column types have a maximum length restriction of 255 characters, regardless 346 346 of whether ``unique=True`` is specified or not. 347 347 348 Row locking with ``QuerySet.select_for_update()`` 349 ------------------------------------------------- 350 351 MySQL does not support the NOWAIT option to the SELECT ... FOR UPDATE 352 statement. However, you may call the ``select_for_update()`` method of a 353 queryset with ``nowait=True``. In that case, the argument will be silently 354 discarded and the generated query will block until the requested lock can be 355 acquired. 356 348 357 .. _sqlite-notes: 349 358 350 359 SQLite notes