Code

Ticket #2705: for_update_1.0.2.patch

File for_update_1.0.2.patch, 8.1 KB (added by jdemoor, 6 years ago)

Fixed MySQL bug.

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

     
    7171        self.order_by = [] 
    7272        self.low_mark, self.high_mark = 0, None  # Used for offset/limit 
    7373        self.distinct = False 
     74        self.select_for_update = False 
     75        self.select_for_update_nowait = False 
    7476        self.select_related = False 
    7577        self.related_select_cols = [] 
    7678 
     
    179181        obj.order_by = self.order_by[:] 
    180182        obj.low_mark, obj.high_mark = self.low_mark, self.high_mark 
    181183        obj.distinct = self.distinct 
     184        obj.select_for_update = self.select_for_update 
     185        obj.select_for_update_nowait = self.select_for_update_nowait 
    182186        obj.select_related = self.select_related 
    183187        obj.related_select_cols = [] 
    184188        obj.max_depth = self.max_depth 
     
    225229        obj = self.clone() 
    226230        obj.clear_ordering(True) 
    227231        obj.clear_limits() 
     232        obj.select_for_update = False 
    228233        obj.select_related = False 
    229234        obj.related_select_cols = [] 
    230235        obj.related_select_fields = [] 
     
    305310                        result.append('LIMIT %d' % val) 
    306311                result.append('OFFSET %d' % self.low_mark) 
    307312 
     313        if self.select_for_update: 
     314            result.append("%s" % self.connection.ops.for_update_sql(nowait=self.select_for_update_nowait)) 
     315 
    308316        params.extend(self.extra_params) 
    309317        return ' '.join(result), tuple(params) 
    310318 
  • django/db/models/manager.py

     
    119119    def order_by(self, *args, **kwargs): 
    120120        return self.get_query_set().order_by(*args, **kwargs) 
    121121 
     122    def select_for_update(self, *args, **kwargs): 
     123        return self.get_query_set().select_for_update(*args, **kwargs) 
     124         
    122125    def select_related(self, *args, **kwargs): 
    123126        return self.get_query_set().select_related(*args, **kwargs) 
    124127 
  • django/db/models/query.py

     
    379379        del_query = self._clone() 
    380380 
    381381        # Disable non-supported fields. 
     382        del_query.query.select_for_update = False 
    382383        del_query.query.select_related = False 
    383384        del_query.query.clear_ordering() 
    384385 
     
    518519        else: 
    519520            return self._filter_or_exclude(None, **filter_obj) 
    520521 
     522    def select_for_update(self, **kwargs): 
     523        """ 
     524        Returns a new QuerySet instance that will select objects with a 
     525        FOR UPDATE lock. 
     526        """ 
     527        # Default to false for nowait 
     528        nowait = kwargs.pop('nowait', False) 
     529        obj = self._clone() 
     530        obj.query.select_for_update = True 
     531        obj.query.select_for_update_nowait = nowait 
     532        return obj 
     533 
    521534    def select_related(self, *fields, **kwargs): 
    522535        """ 
    523536        Returns a new QuerySet instance that will select related objects. 
  • django/db/backends/sqlite3/base.py

     
    6767        # function django_date_trunc that's registered in connect(). 
    6868        return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) 
    6969 
     70    def for_update_sql(self, nowait=False): 
     71        # sqlite does not support FOR UPDATE 
     72        return '' 
     73 
    7074    def drop_foreignkey_sql(self): 
    7175        return "" 
    7276 
     
    202206        return bool(re.search(re_pattern, re_string)) 
    203207    except: 
    204208        return False 
     209 
  • django/db/backends/mysql/base.py

     
    136136    def fulltext_search_sql(self, field_name): 
    137137        return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name 
    138138 
     139    def for_update_sql(self, nowait=False): 
     140        """ 
     141        Return FOR UPDATE SQL clause to lock row for update 
     142        Currently mysql ignores NOWAIT 
     143        """ 
     144        if nowait: 
     145            raise NotImplementedError("NOWAIT option for SELECT ... FOR UPDATE not implemented with mysql"); 
     146        return super(DatabaseOperations, self).for_update_sql(nowait=False) 
     147 
     148 
    139149    def no_limit_value(self): 
    140150        # 2**64 - 1, as recommended by the MySQL documentation 
    141151        return 18446744073709551615L 
  • django/db/backends/__init__.py

     
    143143        """ 
    144144        return '%s' 
    145145 
     146    def for_update_sql(self, nowait=False): 
     147        """ 
     148        Return FOR UPDATE SQL clause to lock row for update 
     149        """ 
     150        if nowait: 
     151            nowaitstr = ' NOWAIT' 
     152        else: 
     153            nowaitstr = '' 
     154        return 'FOR UPDATE' + nowaitstr 
     155 
    146156    def fulltext_search_sql(self, field_name): 
    147157        """ 
    148158        Returns the SQL WHERE clause to use in order to perform a full-text 
  • tests/regressiontests/queries/models.py

     
    227227    def __unicode__(self): 
    228228        return self.name 
    229229 
     230from django.db import transaction 
     231def test_for_update(): 
     232    t = Tag(name='forupdate') 
     233    t.save() 
     234    transaction.commit() 
     235    tfound = Tag.objects.select_for_update().get(pk=t.id) 
     236    tfound.name = 'forupdate2' 
     237    tfound.save() 
     238    transaction.commit() 
     239test_for_update = transaction.commit_manually(test_for_update) 
     240 
    230241__test__ = {'API_TESTS':""" 
    231242>>> t1 = Tag.objects.create(name='t1') 
    232243>>> t2 = Tag.objects.create(name='t2', parent=t1) 
     
    953964>>> len([x[2] for x in q.alias_map.values() if x[2] == q.LOUTER and q.alias_refcount[x[1]]]) 
    9549651 
    955966 
     967Bug #2075 
     968Added FOR UPDATE functionality 
     969>>> test_for_update() 
     970>>> Tag.objects.get(name='forupdate2').delete() 
    956971"""} 
    957972 
    958973# In Python 2.3 and the Python 2.6 beta releases, exceptions raised in __len__ 
     
    9861001[] 
    9871002 
    9881003""" 
     1004 
  • docs/ref/models/querysets.txt

     
    627627 
    628628        Entry.objects.extra(where=['headline=%s'], params=['Lennon']) 
    629629 
     630select_for_update 
     631~~~~~~~~~~~~~~~~~ 
     632 
     633**New in Django development version:** 
     634 
     635Lock rows returned from a query. Most databases allow you to exclusively 
     636lock rows with ``select_for_update()``. To do this call the method 
     637``select_for_update()`` to lock all retrieved objects:: 
     638 
     639    entry = Entry.objects.select_for_update().get(pk=1) 
     640    ... 
     641    entry.save() 
     642 
     643All objects returned using ``select_for_update()`` will be locked with an 
     644exclusive lock which will remain locked until the transaction has finished. 
     645In the case of using middleware, the locks will be released when the view 
     646returns and the transaction is committed or rolled back. SQLite does not 
     647support an exclusive lock so this is simply ignored for SQLite. Other 
     648databases issue a ``FOR UPDATE``. 
     649 
     650If you would not like to wait for the lock then set the parameter nowait to 
     651True. In this case, it will grab the lock if it isn't already locked. If 
     652it is locked then it will throw an exception. This is not supported 
     653with mysql. Doing this with mysql will raise a ``NotImplemented`` exception. 
     654 
     655Note that all rows returned are locked.  If you retrieve multiple objects, 
     656all objects will be locked until the transaction is committed. Another 
     657process which does a ``select_for_update()`` on the same rows will wait until 
     658the transaction which has the locks is finished. 
     659 
    630660QuerySet methods that do not return QuerySets 
    631661--------------------------------------------- 
    632662