hooks/integrity.py
changeset 4490 d45cde54d464
parent 4307 7fba9c34c88f
child 4498 ee6362b4618d
equal deleted inserted replaced
4483:918fd9931cb7 4490:d45cde54d464
     5 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     5 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     8 """
     8 """
     9 __docformat__ = "restructuredtext en"
     9 __docformat__ = "restructuredtext en"
       
    10 
       
    11 from threading import Lock
    10 
    12 
    11 from cubicweb import ValidationError
    13 from cubicweb import ValidationError
    12 from cubicweb.schema import RQLConstraint, RQLUniqueConstraint
    14 from cubicweb.schema import RQLConstraint, RQLUniqueConstraint
    13 from cubicweb.selectors import implements
    15 from cubicweb.selectors import implements
    14 from cubicweb.uilib import soup2xhtml
    16 from cubicweb.uilib import soup2xhtml
    19 DONT_CHECK_RTYPES_ON_ADD = set(('owned_by', 'created_by',
    21 DONT_CHECK_RTYPES_ON_ADD = set(('owned_by', 'created_by',
    20                                 'is', 'is_instance_of',
    22                                 'is', 'is_instance_of',
    21                                 'wf_info_for', 'from_state', 'to_state'))
    23                                 'wf_info_for', 'from_state', 'to_state'))
    22 DONT_CHECK_RTYPES_ON_DEL = set(('is', 'is_instance_of',
    24 DONT_CHECK_RTYPES_ON_DEL = set(('is', 'is_instance_of',
    23                                 'wf_info_for', 'from_state', 'to_state'))
    25                                 'wf_info_for', 'from_state', 'to_state'))
       
    26 
       
    27 _UNIQUE_CONSTRAINTS_LOCK = Lock()
       
    28 _UNIQUE_CONSTRAINTS_HOLDER = None
       
    29 
       
    30 def _acquire_unique_cstr_lock(session):
       
    31     """acquire the _UNIQUE_CONSTRAINTS_LOCK for the session.
       
    32 
       
    33     This lock used to avoid potential integrity pb when checking
       
    34     RQLUniqueConstraint in two different transactions, as explained in
       
    35     http://intranet.logilab.fr/jpl/ticket/36564
       
    36     """
       
    37     global _UNIQUE_CONSTRAINTS_HOLDER
       
    38     asession = session.actual_session()
       
    39     if _UNIQUE_CONSTRAINTS_HOLDER is asession:
       
    40         return
       
    41     _UNIQUE_CONSTRAINTS_LOCK.acquire()
       
    42     _UNIQUE_CONSTRAINTS_HOLDER = asession
       
    43     # register operation responsible to release the lock on commit/rollback
       
    44     _ReleaseUniqueConstraintsOperation(asession)
       
    45 
       
    46 def _release_unique_cstr_lock(session):
       
    47     global _UNIQUE_CONSTRAINTS_HOLDER
       
    48     if _UNIQUE_CONSTRAINTS_HOLDER is session:
       
    49         _UNIQUE_CONSTRAINTS_HOLDER = None
       
    50         _UNIQUE_CONSTRAINTS_LOCK.release()
       
    51     else:
       
    52         assert _UNIQUE_CONSTRAINTS_HOLDER is None
       
    53 
       
    54 class _ReleaseUniqueConstraintsOperation(hook.Operation):
       
    55     def commit_event(self):
       
    56         pass
       
    57     def postcommit_event(self):
       
    58         _release_unique_cstr_lock(self.session)
       
    59     def rollback_event(self):
       
    60         _release_unique_cstr_lock(self.session)
    24 
    61 
    25 
    62 
    26 class _CheckRequiredRelationOperation(hook.LateOperation):
    63 class _CheckRequiredRelationOperation(hook.LateOperation):
    27     """checking relation cardinality has to be done after commit in
    64     """checking relation cardinality has to be done after commit in
    28     case the relation is being replaced
    65     case the relation is being replaced
   124         if self.session.deleted_in_transaction(eidfrom):
   161         if self.session.deleted_in_transaction(eidfrom):
   125             return
   162             return
   126         if self.session.deleted_in_transaction(eidto):
   163         if self.session.deleted_in_transaction(eidto):
   127             return
   164             return
   128         for constraint in self.constraints:
   165         for constraint in self.constraints:
       
   166             # XXX
       
   167             # * lock RQLConstraint as well?
       
   168             # * use a constraint id to use per constraint lock and avoid
       
   169             #   unnecessary commit serialization ?
       
   170             if isinstance(constraint, RQLUniqueConstraint):
       
   171                 _acquire_unique_cstr_lock(self.session)
   129             try:
   172             try:
   130                 constraint.repo_check(self.session, eidfrom, rtype, eidto)
   173                 constraint.repo_check(self.session, eidfrom, rtype, eidto)
   131             except NotImplementedError:
   174             except NotImplementedError:
   132                 self.critical('can\'t check constraint %s, not supported',
   175                 self.critical('can\'t check constraint %s, not supported',
   133                               constraint)
   176                               constraint)