Ticket #2705: for_update_14751.diff

File for_update_14751.diff, 12.3 KB (added by danfairs, 4 years ago)

Update of for_update_1.2.0-final.patch.diff to apply cleanly to trunk r14751

  • django/db/models/sql/compiler.py

     
    117117                        result.append('LIMIT %d' % val)
    118118                result.append('OFFSET %d' % self.query.low_mark)
    119119
     120        if self.query.select_for_update and self.connection.features.has_select_for_update:
     121            nowait = self.query.select_for_update_nowait and self.connection.features.has_select_for_update
     122            result.append("%s" % self.connection.ops.for_update_sql(nowait=nowait))
     123
    120124        return ' '.join(result), tuple(params)
    121125
    122126    def as_nested_sql(self):
  • django/db/models/sql/query.py

     
    1111from django.utils.tree import Node
    1212from django.utils.datastructures import SortedDict
    1313from django.utils.encoding import force_unicode
    14 from django.db import connections, DEFAULT_DB_ALIAS
     14from django.db import connections, DEFAULT_DB_ALIAS, DatabaseError
    1515from django.db.models import signals
    1616from django.db.models.fields import FieldDoesNotExist
    1717from django.db.models.query_utils import select_related_descend, InvalidQuery
     
    2323    ExtraWhere, AND, OR)
    2424from django.core.exceptions import FieldError
    2525
    26 __all__ = ['Query', 'RawQuery']
     26__all__ = ['Query', 'RawQuery', 'LockNotAvailable']
    2727
     28
     29class LockNotAvailable(DatabaseError):
     30    '''
     31    Raised when a query fails because a lock was not available.
     32    '''
     33    pass
     34
     35
    2836class RawQuery(object):
    2937    """
    3038    A single raw SQL query
     
    8492
    8593    def _execute_query(self):
    8694        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
    88101
    89 
    90102class Query(object):
    91103    """
    92104    A single SQL query.
     
    131143        self.order_by = []
    132144        self.low_mark, self.high_mark = 0, None  # Used for offset/limit
    133145        self.distinct = False
     146        self.select_for_update = False
     147        self.select_for_update_nowait = False
    134148        self.select_related = False
    135149        self.related_select_cols = []
    136150
     
    260274        obj.order_by = self.order_by[:]
    261275        obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
    262276        obj.distinct = self.distinct
     277        obj.select_for_update = self.select_for_update
     278        obj.select_for_update_nowait = self.select_for_update_nowait
    263279        obj.select_related = self.select_related
    264280        obj.related_select_cols = []
    265281        obj.aggregates = deepcopy(self.aggregates, memo=memo)
     
    366382
    367383        query.clear_ordering(True)
    368384        query.clear_limits()
     385        query.select_for_update = False
    369386        query.select_related = False
    370387        query.related_select_cols = []
    371388        query.related_select_fields = []
  • django/db/models/manager.py

     
    164164    def order_by(self, *args, **kwargs):
    165165        return self.get_query_set().order_by(*args, **kwargs)
    166166
     167    def select_for_update(self, *args, **kwargs):
     168        return self.get_query_set().select_for_update(*args, **kwargs)
     169
    167170    def select_related(self, *args, **kwargs):
    168171        return self.get_query_set().select_related(*args, **kwargs)
    169172
  • django/db/models/query.py

     
    432432        del_query._for_write = True
    433433
    434434        # Disable non-supported fields.
     435        del_query.query.select_for_update = False
    435436        del_query.query.select_related = False
    436437        del_query.query.clear_ordering()
    437438
     
    580581        else:
    581582            return self._filter_or_exclude(None, **filter_obj)
    582583
     584    def select_for_update(self, **kwargs):
     585        """
     586        Returns a new QuerySet instance that will select objects with a
     587        FOR UPDATE lock.
     588        """
     589        # Default to false for nowait
     590        nowait = kwargs.pop('nowait', False)
     591        obj = self._clone()
     592        obj.query.select_for_update = True
     593        obj.query.select_for_update_nowait = nowait
     594        return obj
     595
    583596    def select_related(self, *fields, **kwargs):
    584597        """
    585598        Returns a new QuerySet instance that will select related objects.
  • django/db/backends/mysql/base.py

     
    2323    raise ImproperlyConfigured("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__)
    2424
    2525from MySQLdb.converters import conversions
    26 from MySQLdb.constants import FIELD_TYPE, FLAG, CLIENT
     26from MySQLdb.constants import FIELD_TYPE, FLAG, CLIENT, ER
    2727
    2828from django.db import utils
    2929from django.db.backends import *
     
    124124    allows_group_by_pk = True
    125125    related_fields_match_type = True
    126126    allow_sliced_subqueries = False
     127    has_select_for_update = True
     128    has_select_for_update_nowait = False
    127129    supports_forward_references = False
    128130    supports_long_model_names = False
    129131    supports_microsecond_precision = False
     
    135137
    136138class DatabaseOperations(BaseDatabaseOperations):
    137139    compiler_module = "django.db.backends.mysql.compiler"
     140    signals_deadlock = lambda self, e: e.args[0] == ER.LOCK_DEADLOCK
    138141
    139142    def date_extract_sql(self, lookup_type, field_name):
    140143        # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
  • django/db/backends/oracle/base.py

     
    4848    needs_datetime_string_cast = False
    4949    interprets_empty_strings_as_nulls = True
    5050    uses_savepoints = True
     51    has_select_for_update = True
     52    has_select_for_update_nowait = True
    5153    can_return_id_from_insert = True
    5254    allow_sliced_subqueries = False
    5355    supports_subqueries_in_group_by = False
     
    285287                                           'column': column_name})
    286288        return output
    287289
     290    def signals_deadlock(self, exception):
     291        return exception.args[0].code == 60
     292
     293    def signals_lock_not_available(self, exception):
     294        return exception.args[0].code == 54
     295
    288296    def start_transaction_sql(self):
    289297        return ''
    290298
  • django/db/backends/__init__.py

     
    103103    # integer primary keys.
    104104    related_fields_match_type = False
    105105    allow_sliced_subqueries = True
     106    has_select_for_update = False
     107    has_select_for_update_nowait = False
    106108
    107109    # Does the default test database allow multiple connections?
    108110    # Usually an indication that the test database is in-memory
     
    281283        ordering.
    282284        """
    283285        return []
     286    def for_update_sql(self, nowait=False):
     287        """
     288        Return FOR UPDATE SQL clause to lock row for update
     289        """
     290        if nowait:
     291            nowaitstr = ' NOWAIT'
     292        else:
     293            nowaitstr = ''
     294        return 'FOR UPDATE' + nowaitstr
    284295
    285296    def fulltext_search_sql(self, field_name):
    286297        """
  • django/db/backends/postgresql_psycopg2/base.py

     
    1919try:
    2020    import psycopg2 as Database
    2121    import psycopg2.extensions
     22    from psycopg2 import errorcodes
    2223except ImportError, e:
    2324    from django.core.exceptions import ImproperlyConfigured
    2425    raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e)
     
    7071    requires_rollback_on_dirty_transaction = True
    7172    has_real_datatype = True
    7273    can_defer_constraint_checks = True
     74    has_select_for_update = True
     75    has_select_for_update_nowait = True
     76   
    7377
    7478class DatabaseOperations(PostgresqlDatabaseOperations):
     79    signals_deadlock = lambda self, e: e.pgcode == errorcodes.DEADLOCK_DETECTED
     80    signals_lock_not_available = lambda self, e: e.pgcode == errorcodes.LOCK_NOT_AVAILABLE
     81   
    7582    def last_executed_query(self, cursor, sql, params):
    7683        # With psycopg2, cursor objects have a "query" attribute that is the
    7784        # exact query sent to the database. See docs here:
  • docs/ref/models/querysets.txt

     
    975975    # queries the database with the 'backup' alias
    976976    >>> Entry.objects.using('backup')
    977977
     978``select_for_update(nowait=False)``
     979~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     980         
     981Returns a queryset that will lock rows until the end of the transaction, 
     982generating a SELECT ... FOR UPDATE statement on supported databases.
     983         
     984For example::
     985         
     986    entries = Entry.objects.select_for_update().filter(author=request.user)
     987 
     988All matched entries will be locked until the end of the transaction block, 
     989meaning that other transactions will be prevented from changing or acquiring 
     990locks on them.
     991 
     992Usually, if another transaction has already acquired a lock on one of the 
     993selected rows, the query will block until the lock is released. If this is 
     994not the behaviour you want, call ``select_for_update(nowait=True)``. This will 
     995make the call non-blocking. If a conflicting lock is already acquired by 
     996another transaction, ``django.db.models.LockNotAvailable`` will be raised when 
     997the queryset is evaluated.
     998 
     999Using blocking locks on a database can lead to deadlocks. This occurs when two 
     1000concurrent transactions are both waiting on a lock the other transaction 
     1001already holds. To deal with deadlocks, wrap your views that use 
     1002``select_for_update(nowait=False)`` with the 
     1003``django.views.decorators.deadlock.handle_deadlocks`` decorator. 
     1004 
     1005For example::
     1006 
     1007    from django.db import transaction
     1008    from django.views.decorators.deadlock import handle_deadlocks
     1009 
     1010    @handle_deadlocks(max_retries=2)
     1011    @transaction.commit_on_success
     1012    def my_view(request):
     1013        ...
     1014 
     1015If the database engine detects a deadlock involving ``my_view`` and decides 
     1016to abort its transaction, it will be automatically retried. If deadlocks keep 
     1017occurring after two repeated attempts, 
     1018``django.views.decorators.DeadlockError`` will be raised, which can be 
     1019propagated to the user or handled in a middleware.
     1020 
     1021Currently the ``postgresql_psycopg2``, ``oracle``, and ``mysql``
     1022database backends support ``select_for_update()`` but MySQL has no
     1023support for the ``nowait`` argument. Other backends will simply
     1024generate queries as if ``select_for_update()`` had not been used.
    9781025
    9791026Methods that do not return QuerySets
    9801027------------------------------------
  • docs/ref/databases.txt

     
    362362column types have a maximum length restriction of 255 characters, regardless
    363363of whether ``unique=True`` is specified or not.
    364364
     365Row locking with ``QuerySet.select_for_update()``
     366-------------------------------------------------
     367 
     368MySQL does not support the NOWAIT option to the SELECT ... FOR UPDATE 
     369statement. However, you may call the ``select_for_update()`` method of a 
     370queryset with ``nowait=True``. In that case, the argument will be silently 
     371discarded and the generated query will block until the requested lock can be 
     372acquired.
     373
    365374.. _sqlite-notes:
    366375
    367376SQLite notes
Back to Top