Ticket #2705: for_update_11366_cdestigter.diff

File for_update_11366_cdestigter.diff, 12.1 KB (added by cdestigter, 5 years ago)

remove unnecessary/broken changes to django/db/models/base.py

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

     
    1313from django.utils.datastructures import SortedDict
    1414from django.utils.encoding import force_unicode
    1515from django.db.backends.util import truncate_name
    16 from django.db import connection
     16from django.db import connection, DatabaseError
    1717from django.db.models import signals
    1818from django.db.models.fields import FieldDoesNotExist
    1919from django.db.models.query_utils import select_related_descend
     
    2929except NameError:
    3030    from sets import Set as set     # Python 2.3 fallback
    3131
    32 __all__ = ['Query', 'BaseQuery']
     32__all__ = ['Query', 'BaseQuery', 'LockNotAvailable']
    3333
     34class LockNotAvailable(DatabaseError):
     35    '''
     36    Raised when a query fails because a lock was not available.
     37    '''
     38    pass
     39
    3440class BaseQuery(object):
    3541    """
    3642    A single SQL query.
     
    7480        self.order_by = []
    7581        self.low_mark, self.high_mark = 0, None  # Used for offset/limit
    7682        self.distinct = False
     83        self.select_for_update = False
     84        self.select_for_update_nowait = False
    7785        self.select_related = False
    7886        self.related_select_cols = []
    7987
     
    211219        obj.order_by = self.order_by[:]
    212220        obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
    213221        obj.distinct = self.distinct
     222        obj.select_for_update = self.select_for_update
     223        obj.select_for_update_nowait = self.select_for_update_nowait
    214224        obj.select_related = self.select_related
    215225        obj.related_select_cols = []
    216226        obj.aggregates = deepcopy(self.aggregates)
     
    341351
    342352        query.clear_ordering(True)
    343353        query.clear_limits()
     354        query.select_for_update = False
    344355        query.select_related = False
    345356        query.related_select_cols = []
    346357        query.related_select_fields = []
     
    459470                        result.append('LIMIT %d' % val)
    460471                result.append('OFFSET %d' % self.low_mark)
    461472
     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
    462477        params.extend(self.extra_params)
    463478        return ' '.join(result), tuple(params)
    464479
     
    23662381            else:
    23672382                return
    23682383        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
    23702390
    23712391        if not result_type:
    23722392            return cursor
  • django/db/models/manager.py

     
    152152    def order_by(self, *args, **kwargs):
    153153        return self.get_query_set().order_by(*args, **kwargs)
    154154
     155    def select_for_update(self, *args, **kwargs):
     156        return self.get_query_set().select_for_update(*args, **kwargs)
     157       
    155158    def select_related(self, *args, **kwargs):
    156159        return self.get_query_set().select_related(*args, **kwargs)
    157160
  • django/db/models/__init__.py

     
    1111from django.db.models.fields.subclassing import SubfieldBase
    1212from django.db.models.fields.files import FileField, ImageField
    1313from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel
     14from django.db.models.sql.query import LockNotAvailable
    1415from django.db.models import signals
    1516
    1617# Admin stages.
  • django/db/models/query.py

     
    381381        del_query = self._clone()
    382382
    383383        # Disable non-supported fields.
     384        del_query.query.select_for_update = False
    384385        del_query.query.select_related = False
    385386        del_query.query.clear_ordering()
    386387
     
    533534        else:
    534535            return self._filter_or_exclude(None, **filter_obj)
    535536
     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
    536550    def select_related(self, *fields, **kwargs):
    537551        """
    538552        Returns a new QuerySet instance that will select related objects.
  • django/db/backends/mysql/base.py

     
    113113    update_can_self_select = False
    114114    allows_group_by_pk = True
    115115    related_fields_match_type = True
     116    has_select_for_update = True
     117    has_select_for_update_nowait = False
    116118
    117119class DatabaseOperations(BaseDatabaseOperations):
    118120    def date_extract_sql(self, lookup_type, field_name):
     
    208210        # MySQL doesn't support microseconds
    209211        return unicode(value.replace(microsecond=0))
    210212
     213    signals_deadlock = lambda self, e: e.args[0] == ER.LOCK_DEADLOCK
     214
    211215    def year_lookup_bounds(self, value):
    212216        # Again, no microseconds
    213217        first = '%s-01-01 00:00:00'
  • django/db/backends/oracle/base.py

     
    4141    needs_datetime_string_cast = False
    4242    uses_custom_query_class = True
    4343    interprets_empty_strings_as_nulls = True
     44    has_select_for_update = True
     45    has_select_for_update_nowait = True
    4446    uses_savepoints = True
    4547    can_return_id_from_insert = True
    4648
     
    226228                                           'column': column_name})
    227229        return output
    228230
     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
    229237    def start_transaction_sql(self):
    230238        return ''
    231239
  • django/db/backends/__init__.py

     
    102102    # If True, don't use integer foreign keys referring to, e.g., positive
    103103    # integer primary keys.
    104104    related_fields_match_type = False
     105    has_select_for_update = False
     106    has_select_for_update_nowait = False
    105107
    106108class BaseDatabaseOperations(object):
    107109    """
     
    187189        """
    188190        return []
    189191
     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
    190202    def fulltext_search_sql(self, field_name):
    191203        """
    192204        Returns the SQL WHERE clause to use in order to perform a full-text
  • django/db/backends/postgresql_psycopg2/base.py

     
    1717try:
    1818    import psycopg2 as Database
    1919    import psycopg2.extensions
     20    from psycopg2 import errorcodes
    2021except ImportError, e:
    2122    from django.core.exceptions import ImproperlyConfigured
    2223    raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e)
     
    3031
    3132class DatabaseFeatures(BaseDatabaseFeatures):
    3233    needs_datetime_string_cast = False
     34    has_select_for_update = True
     35    has_select_for_update_nowait = True
    3336    can_return_id_from_insert = False
    3437
    3538class DatabaseOperations(PostgresqlDatabaseOperations):
     
    4245    def return_insert_id(self):
    4346        return "RETURNING %s", ()
    4447
     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
    4553class DatabaseWrapper(BaseDatabaseWrapper):
    4654    operators = {
    4755        'exact': '= %s',
  • docs/ref/models/querysets.txt

     
    875875    Entry.objects.defer("body").only("headline", "body")
    876876
    877877
     878``select_for_update(nowait=False)``
     879~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     880
     881Returns a queryset that will lock rows until the end of the transaction,
     882generating a SELECT ... FOR UPDATE statement on supported databases.
     883
     884For example::
     885
     886    entries = Entry.objects.select_for_update().filter(author=request.user)
     887
     888All matched entries will be locked until the end of the transaction block,
     889meaning that other transactions will be prevented from changing or acquiring
     890locks on them.
     891
     892Usually, if another transaction has already acquired a lock on one of the
     893selected rows, the query will block until the lock is released. If this is
     894not the behaviour you want, call ``select_for_update(nowait=True)``. This will
     895make the call non-blocking. If a conflicting lock is already acquired by
     896another transaction, ``django.db.models.LockNotAvailable`` will be raised when
     897the queryset is evaluated.
     898
     899Using blocking locks on a database can lead to deadlocks. This occurs when two
     900concurrent transactions are both waiting on a lock the other transaction
     901already 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
     905For 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
     915If the database engine detects a deadlock involving ``my_view`` and decides
     916to abort its transaction, it will be automatically retried. If deadlocks keep
     917occurring after two repeated attempts,
     918``django.views.decorators.DeadlockError`` will be raised, which can be
     919propagated to the user or handled in a middleware.
     920
     921Currently the ``postgresql_psycopg2``, ``oracle``, and ``mysql``
     922database backends support ``select_for_update()`` but MySQL has no
     923support for the ``nowait`` argument. Other backends will simply
     924generate queries as if ``select_for_update()`` had not been used.
     925
    878926QuerySet methods that do not return QuerySets
    879927---------------------------------------------
    880928
  • docs/ref/databases.txt

     
    325325column types have a maximum length restriction of 255 characters, regardless
    326326of whether ``unique=True`` is specified or not.
    327327
     328Row locking with ``QuerySet.select_for_update()``
     329-------------------------------------------------
     330
     331MySQL does not support the NOWAIT option to the SELECT ... FOR UPDATE
     332statement. However, you may call the ``select_for_update()`` method of a
     333queryset with ``nowait=True``. In that case, the argument will be silently
     334discarded and the generated query will block until the requested lock can be
     335acquired.
     336
    328337.. _sqlite-notes:
    329338
    330339SQLite notes
Back to Top