hooks/integrity.py
changeset 9548 be001628edad
parent 9469 032825bbacab
child 9613 45370ea9f495
--- a/hooks/integrity.py	Fri Jan 24 13:08:53 2014 +0100
+++ b/hooks/integrity.py	Tue Jan 21 18:30:16 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -24,10 +24,10 @@
 
 from threading import Lock
 
-from cubicweb import validation_error
+from cubicweb import validation_error, neg_role
 from cubicweb.schema import (META_RTYPES, WORKFLOW_RTYPES,
                              RQLConstraint, RQLUniqueConstraint)
-from cubicweb.predicates import is_instance
+from cubicweb.predicates import is_instance, composite_etype
 from cubicweb.uilib import soup2xhtml
 from cubicweb.server import hook
 
@@ -309,69 +309,25 @@
             self.entity.cw_edited['login'] = login.strip()
 
 
-# 'active' integrity hooks: you usually don't want to deactivate them, they are
-# not really integrity check, they maintain consistency on changes
-
-class _DelayedDeleteOp(hook.DataOperationMixIn, hook.Operation):
-    """delete the object of composite relation except if the relation has
-    actually been redirected to another composite
-    """
-    base_rql = None
-
-    def precommit_event(self):
-        session = self.session
-        pendingeids = session.transaction_data.get('pendingeids', ())
-        eids_by_etype_rtype = {}
-        for eid, rtype in self.get_data():
-            # don't do anything if the entity is being deleted
-            if eid not in pendingeids:
-                etype = session.entity_metas(eid)['type']
-                key = (etype, rtype)
-                if key not in eids_by_etype_rtype:
-                    eids_by_etype_rtype[key] = [str(eid)]
-                else:
-                    eids_by_etype_rtype[key].append(str(eid))
-        for (etype, rtype), eids in eids_by_etype_rtype.iteritems():
-            # quite unexpectedly, not deleting too many entities at a time in
-            # this operation benefits to the exec speed (possibly on the RQL
-            # parsing side)
-            start = 0
-            incr = 500
-            while start < len(eids):
-                session.execute(self.base_rql % (etype, ','.join(eids[start:start+incr]), rtype))
-                start += incr
-
-class _DelayedDeleteSEntityOp(_DelayedDeleteOp):
-    """delete orphan subject entity of a composite relation"""
-    base_rql = 'DELETE %s X WHERE X eid IN (%s), NOT X %s Y'
-
-class _DelayedDeleteOEntityOp(_DelayedDeleteOp):
-    """check required object relation"""
-    base_rql = 'DELETE %s X WHERE X eid IN (%s), NOT Y %s X'
-
-
 class DeleteCompositeOrphanHook(hook.Hook):
-    """delete the composed of a composite relation when this relation is deleted
+    """Delete the composed of a composite relation when the composite is
+    deleted (this is similar to the cascading ON DELETE CASCADE
+    semantics of sql).
     """
     __regid__ = 'deletecomposite'
-    events = ('before_delete_relation',)
+    __select__ = hook.Hook.__select__ & composite_etype()
+    events = ('before_delete_entity',)
     category = 'activeintegrity'
 
     def __call__(self):
-        # if the relation is being delete, don't delete composite's components
-        # automatically
-        session = self._cw
-        rtype = self.rtype
-        rdef = session.rtype_eids_rdef(rtype, self.eidfrom, self.eidto)
-        if (rdef.subject, rtype, rdef.object) in session.transaction_data.get('pendingrdefs', ()):
-            return
-        composite = rdef.composite
-        if composite == 'subject':
-            _DelayedDeleteOEntityOp.get_instance(self._cw).add_data(
-                (self.eidto, rtype))
-        elif composite == 'object':
-            _DelayedDeleteSEntityOp.get_instance(self._cw).add_data(
-                (self.eidfrom, rtype))
+        eid = self.entity.eid
+        for rdef, role in self.entity.e_schema.composite_rdef_roles:
+            rtype = rdef.rtype.type
+            target = getattr(rdef, neg_role(role))
+            expr = ('C %s X' % rtype) if role == 'subject' else ('X %s C' % rtype)
+            self._cw.execute('DELETE %s X WHERE C eid %%(c)s, %s' % (target, expr),
+                             {'c': eid})
+
 
 def registration_callback(vreg):
     vreg.register_all(globals().values(), __name__)