108 class IntegrityHook(hook.Hook): |
108 class IntegrityHook(hook.Hook): |
109 __abstract__ = True |
109 __abstract__ = True |
110 category = 'integrity' |
110 category = 'integrity' |
111 |
111 |
112 |
112 |
113 class CheckCardinalityHook(IntegrityHook): |
113 class CheckCardinalityHookBeforeDeleteRelation(IntegrityHook): |
114 """check cardinalities are satisfied""" |
114 """check cardinalities are satisfied""" |
115 __regid__ = 'checkcard' |
115 __regid__ = 'checkcard_before_delete_relation' |
116 events = ('after_add_entity', 'before_delete_relation') |
116 events = ('before_delete_relation',) |
117 |
117 |
118 def __call__(self): |
118 def __call__(self): |
119 getattr(self, self.event)() |
119 rtype = self.rtype |
120 |
120 if rtype in DONT_CHECK_RTYPES_ON_DEL: |
121 def after_add_entity(self): |
121 return |
|
122 session = self._cw |
|
123 eidfrom, eidto = self.eidfrom, self.eidto |
|
124 pendingrdefs = session.transaction_data.get('pendingrdefs', ()) |
|
125 if (session.describe(eidfrom)[0], rtype, session.describe(eidto)[0]) in pendingrdefs: |
|
126 return |
|
127 card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality') |
|
128 if card[0] in '1+' and not session.deleted_in_transaction(eidfrom): |
|
129 _CheckSRelationOp.get_instance(self._cw).add_data((eidfrom, rtype)) |
|
130 if card[1] in '1+' and not session.deleted_in_transaction(eidto): |
|
131 _CheckORelationOp.get_instance(self._cw).add_data((eidto, rtype)) |
|
132 |
|
133 class CheckCardinalityHookAfterAddEntity(IntegrityHook): |
|
134 """check cardinalities are satisfied""" |
|
135 __regid__ = 'checkcard_after_add_entity' |
|
136 events = ('after_add_entity',) |
|
137 |
|
138 def __call__(self): |
122 eid = self.entity.eid |
139 eid = self.entity.eid |
123 eschema = self.entity.e_schema |
140 eschema = self.entity.e_schema |
124 for rschema, targetschemas, role in eschema.relation_definitions(): |
141 for rschema, targetschemas, role in eschema.relation_definitions(): |
125 # skip automatically handled relations |
142 # skip automatically handled relations |
126 if rschema.type in DONT_CHECK_RTYPES_ON_ADD: |
143 if rschema.type in DONT_CHECK_RTYPES_ON_ADD: |
297 |
314 |
298 def precommit_event(self): |
315 def precommit_event(self): |
299 session = self.session |
316 session = self.session |
300 pendingeids = session.transaction_data.get('pendingeids', ()) |
317 pendingeids = session.transaction_data.get('pendingeids', ()) |
301 neweids = session.transaction_data.get('neweids', ()) |
318 neweids = session.transaction_data.get('neweids', ()) |
|
319 eids_by_etype_rtype = {} |
302 for eid, rtype in self.get_data(): |
320 for eid, rtype in self.get_data(): |
303 # don't do anything if the entity is being created or deleted |
321 # don't do anything if the entity is being created or deleted |
304 if not (eid in pendingeids or eid in neweids): |
322 if not (eid in pendingeids or eid in neweids): |
305 etype = session.describe(eid)[0] |
323 etype = session.describe(eid)[0] |
306 session.execute(self.base_rql % (etype, rtype), {'x': eid}) |
324 key = (etype, rtype) |
|
325 if key not in eids_by_etype_rtype: |
|
326 eids_by_etype_rtype[key] = [str(eid)] |
|
327 else: |
|
328 eids_by_etype_rtype[key].append(str(eid)) |
|
329 for (etype, rtype), eids in eids_by_etype_rtype.iteritems(): |
|
330 # quite unexpectedly, not deleting too many entities at a time in |
|
331 # this operation benefits to the exec speed (possibly on the RQL |
|
332 # parsing side) |
|
333 start = 0 |
|
334 incr = 500 |
|
335 while start < len(eids): |
|
336 session.execute(self.base_rql % (etype, ','.join(eids[start:start+incr]), rtype)) |
|
337 start += incr |
307 |
338 |
308 class _DelayedDeleteSEntityOp(_DelayedDeleteOp): |
339 class _DelayedDeleteSEntityOp(_DelayedDeleteOp): |
309 """delete orphan subject entity of a composite relation""" |
340 """delete orphan subject entity of a composite relation""" |
310 base_rql = 'DELETE %s X WHERE X eid %%(x)s, NOT X %s Y' |
341 base_rql = 'DELETE %s X WHERE X eid IN (%s), NOT X %s Y' |
311 |
342 |
312 class _DelayedDeleteOEntityOp(_DelayedDeleteOp): |
343 class _DelayedDeleteOEntityOp(_DelayedDeleteOp): |
313 """check required object relation""" |
344 """check required object relation""" |
314 base_rql = 'DELETE %s X WHERE X eid %%(x)s, NOT Y %s X' |
345 base_rql = 'DELETE %s X WHERE X eid IN (%s), NOT Y %s X' |
315 |
346 |
316 |
347 |
317 class DeleteCompositeOrphanHook(hook.Hook): |
348 class DeleteCompositeOrphanHook(hook.Hook): |
318 """delete the composed of a composite relation when this relation is deleted |
349 """delete the composed of a composite relation when this relation is deleted |
319 """ |
350 """ |