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) |