[repo integrity] test and fix glob add relation where several entities are added at once for a relation of 1? cardinality stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 15 Jun 2011 17:14:40 +0200
branchstable
changeset 7513 8f4422391e5a
parent 7512 d2de5fb0cc33
child 7514 32081892850e
child 7515 e1ba23fdcf2d
[repo integrity] test and fix glob add relation where several entities are added at once for a relation of 1? cardinality
server/repository.py
server/test/unittest_repository.py
--- a/server/repository.py	Wed Jun 15 17:13:26 2011 +0200
+++ b/server/repository.py	Wed Jun 15 17:14:40 2011 +0200
@@ -1384,33 +1384,63 @@
         relations is a dictionary rtype: [(subj_eid, obj_eid), ...]
         """
         sources = {}
+        subjects_by_types = {}
+        objects_by_types = {}
+        activintegrity = session.is_hook_category_activated('activeintegrity')
         for rtype, eids_subj_obj in relations.iteritems():
             if server.DEBUG & server.DBG_REPO:
-                for subject, object in relations:
-                    print 'ADD relation', subject, rtype, object
-            for subject, object in eids_subj_obj:
-                source = self.locate_relation_source(session, subject, rtype, object)
+                for subjeid, objeid in relations:
+                    print 'ADD relation', subjeid, rtype, objeid
+            for subjeid, objeid in eids_subj_obj:
+                source = self.locate_relation_source(session, subjeid, rtype, objeid)
                 if source not in sources:
                     relations_by_rtype = {}
                     sources[source] = relations_by_rtype
                 else:
                     relations_by_rtype = sources[source]
                 if rtype in relations_by_rtype:
-                    relations_by_rtype[rtype].append((subject, object))
+                    relations_by_rtype[rtype].append((subjeid, objeid))
                 else:
-                    relations_by_rtype[rtype] = [(subject, object)]
+                    relations_by_rtype[rtype] = [(subjeid, objeid)]
+                if not activintegrity:
+                    continue
+                # take care to relation of cardinality '?1', as all eids will
+                # be inserted later, we've remove duplicated eids since they
+                # won't be catched by `del_existing_rel_if_needed`
+                rdef = session.rtype_eids_rdef(rtype, subjeid, objeid)
+                card = rdef.cardinality
+                if card[0] in '?1':
+                    with security_enabled(session, read=False):
+                        session.execute('DELETE X %s Y WHERE X eid %%(x)s, '
+                                        'NOT Y eid %%(y)s' % rtype,
+                                        {'x': subjeid, 'y': objeid})
+                    subjects = subjects_by_types.setdefault(rdef, {})
+                    if subjeid in subjects:
+                        del relations_by_rtype[rtype][subjects[subjeid]]
+                        subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
+                        continue
+                    subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
+                if card[1] in '?1':
+                    with security_enabled(session, read=False):
+                        session.execute('DELETE X %s Y WHERE Y eid %%(y)s, '
+                                        'NOT X eid %%(x)s' % rtype,
+                                        {'x': subjeid, 'y': objeid})
+                    objects = objects_by_types.setdefault(rdef, {})
+                    if objeid in objects:
+                        del relations_by_rtype[rtype][objects[objeid]]
+                        objects[objeid] = len(relations_by_rtype[rtype])
+                        continue
+                    objects[objeid] = len(relations_by_rtype[rtype])
         for source, relations_by_rtype in sources.iteritems():
             if source.should_call_hooks:
                 for rtype, source_relations in relations_by_rtype.iteritems():
-                    for subject, object in source_relations:
-                        del_existing_rel_if_needed(session, subject, rtype, object)
                     self.hm.call_hooks('before_add_relation', session,
                                     rtype=rtype, eids_from_to=source_relations)
             for rtype, source_relations in relations_by_rtype.iteritems():
                 source.add_relations(session, rtype, source_relations)
                 rschema = self.schema.rschema(rtype)
-                for subject, object in source_relations:
-                    session.update_rel_cache_add(subject, rtype, object, rschema.symmetric)
+                for subjeid, objeid in source_relations:
+                    session.update_rel_cache_add(subjeid, rtype, objeid, rschema.symmetric)
             if source.should_call_hooks:
                 for rtype, source_relations in relations_by_rtype.iteritems():
                     self.hm.call_hooks('after_add_relation', session,
--- a/server/test/unittest_repository.py	Wed Jun 15 17:13:26 2011 +0200
+++ b/server/test/unittest_repository.py	Wed Jun 15 17:14:40 2011 +0200
@@ -841,6 +841,29 @@
         t1 = time.time()
         self.info('add relations (inlined): %.2gs', t1-t0)
 
+    def test_optional_relation_reset_1(self):
+        req = self.request()
+        p1 = req.create_entity('Personne', nom=u'Vincent')
+        p2 = req.create_entity('Personne', nom=u'Florent')
+        w = req.create_entity('Affaire', ref=u'wc')
+        w.set_relations(todo_by=[p1,p2])
+        w.clear_all_caches()
+        self.commit()
+        self.assertEqual(len(w.todo_by), 1)
+        self.assertEqual(w.todo_by[0].eid, p2.eid)
+
+    def test_optional_relation_reset_2(self):
+        req = self.request()
+        p1 = req.create_entity('Personne', nom=u'Vincent')
+        p2 = req.create_entity('Personne', nom=u'Florent')
+        w = req.create_entity('Affaire', ref=u'wc')
+        w.set_relations(todo_by=p1)
+        self.commit()
+        w.set_relations(todo_by=p2)
+        w.clear_all_caches()
+        self.commit()
+        self.assertEqual(len(w.todo_by), 1)
+        self.assertEqual(w.todo_by[0].eid, p2.eid)
 
 
 if __name__ == '__main__':