server/repository.py
changeset 9454 a173f9cf9f26
parent 9452 5308b3fe03c9
child 9455 62e89e696a3b
equal deleted inserted replaced
9453:5c4d9dfbf176 9454:a173f9cf9f26
  1213                         raise
  1213                         raise
  1214                     self.exception('error while cascading delete for entity %s. RQL: %s',
  1214                     self.exception('error while cascading delete for entity %s. RQL: %s',
  1215                                    entities, rql)
  1215                                    entities, rql)
  1216         self.system_source.delete_info_multi(session, entities)
  1216         self.system_source.delete_info_multi(session, entities)
  1217 
  1217 
  1218     def locate_relation_source(self, session, subject, rtype, object):
       
  1219         subjsource = self.source_from_eid(subject, session)
       
  1220         objsource = self.source_from_eid(object, session)
       
  1221         if not subjsource.support_relation(rtype):
       
  1222             source = self.system_source
       
  1223         else:
       
  1224             source = subjsource
       
  1225         if not source.support_relation(rtype, True):
       
  1226             raise MultiSourcesError(
       
  1227                 "source %s doesn't support write of %s relation"
       
  1228                 % (source.uri, rtype))
       
  1229         return source
       
  1230 
       
  1231     def locate_etype_source(self, etype):
       
  1232         for source in self.sources:
       
  1233             if source.support_entity(etype, 1):
       
  1234                 return source
       
  1235         else:
       
  1236             raise ETypeNotSupportedBySources(etype)
       
  1237 
       
  1238     def init_entity_caches(self, session, entity, source):
  1218     def init_entity_caches(self, session, entity, source):
  1239         """add entity to session entities cache and repo's extid cache.
  1219         """add entity to session entities cache and repo's extid cache.
  1240         Return entity's ext id if the source isn't the system source.
  1220         Return entity's ext id if the source isn't the system source.
  1241         """
  1221         """
  1242         session.set_entity_cache(entity)
  1222         session.set_entity_cache(entity)
  1259         """
  1239         """
  1260         entity = edited.entity
  1240         entity = edited.entity
  1261         entity._cw_is_saved = False # entity has an eid but is not yet saved
  1241         entity._cw_is_saved = False # entity has an eid but is not yet saved
  1262         # init edited_attributes before calling before_add_entity hooks
  1242         # init edited_attributes before calling before_add_entity hooks
  1263         entity.cw_edited = edited
  1243         entity.cw_edited = edited
  1264         source = self.locate_etype_source(entity.cw_etype)
  1244         source = self.system_source
  1265         # allocate an eid to the entity before calling hooks
  1245         # allocate an eid to the entity before calling hooks
  1266         entity.eid = self.system_source.create_eid(session)
  1246         entity.eid = self.system_source.create_eid(session)
  1267         # set caches asap
  1247         # set caches asap
  1268         extid = self.init_entity_caches(session, entity, source)
  1248         extid = self.init_entity_caches(session, entity, source)
  1269         if server.DEBUG & server.DBG_REPO:
  1249         if server.DEBUG & server.DBG_REPO:
  1270             print 'ADD entity', self, entity.cw_etype, entity.eid, edited
  1250             print 'ADD entity', self, entity.cw_etype, entity.eid, edited
  1271         prefill_entity_caches(entity)
  1251         prefill_entity_caches(entity)
  1272         if source.should_call_hooks:
  1252         self.hm.call_hooks('before_add_entity', session, entity=entity)
  1273             self.hm.call_hooks('before_add_entity', session, entity=entity)
       
  1274         relations = preprocess_inlined_relations(session, entity)
  1253         relations = preprocess_inlined_relations(session, entity)
  1275         edited.set_defaults()
  1254         edited.set_defaults()
  1276         if session.is_hook_category_activated('integrity'):
  1255         if session.is_hook_category_activated('integrity'):
  1277             edited.check(creation=True)
  1256             edited.check(creation=True)
  1278         try:
  1257         try:
  1282                 'IUserFriendlyError', session, entity=entity, exc=exc)
  1261                 'IUserFriendlyError', session, entity=entity, exc=exc)
  1283             userhdlr.raise_user_exception()
  1262             userhdlr.raise_user_exception()
  1284         self.add_info(session, entity, source, extid, complete=False)
  1263         self.add_info(session, entity, source, extid, complete=False)
  1285         edited.saved = entity._cw_is_saved = True
  1264         edited.saved = entity._cw_is_saved = True
  1286         # trigger after_add_entity after after_add_relation
  1265         # trigger after_add_entity after after_add_relation
  1287         if source.should_call_hooks:
  1266         self.hm.call_hooks('after_add_entity', session, entity=entity)
  1288             self.hm.call_hooks('after_add_entity', session, entity=entity)
  1267         # call hooks for inlined relations
  1289             # call hooks for inlined relations
  1268         for attr, value in relations:
  1290             for attr, value in relations:
  1269             self.hm.call_hooks('before_add_relation', session,
  1291                 self.hm.call_hooks('before_add_relation', session,
  1270                                 eidfrom=entity.eid, rtype=attr, eidto=value)
  1292                                     eidfrom=entity.eid, rtype=attr, eidto=value)
  1271             self.hm.call_hooks('after_add_relation', session,
  1293                 self.hm.call_hooks('after_add_relation', session,
  1272                                 eidfrom=entity.eid, rtype=attr, eidto=value)
  1294                                     eidfrom=entity.eid, rtype=attr, eidto=value)
       
  1295         return entity.eid
  1273         return entity.eid
  1296 
  1274 
  1297     def glob_update_entity(self, session, edited):
  1275     def glob_update_entity(self, session, edited):
  1298         """replace an entity in the repository
  1276         """replace an entity in the repository
  1299         the type and the eid of an entity must not be changed
  1277         the type and the eid of an entity must not be changed
  1305         hm = self.hm
  1283         hm = self.hm
  1306         eschema = entity.e_schema
  1284         eschema = entity.e_schema
  1307         session.set_entity_cache(entity)
  1285         session.set_entity_cache(entity)
  1308         orig_edited = getattr(entity, 'cw_edited', None)
  1286         orig_edited = getattr(entity, 'cw_edited', None)
  1309         entity.cw_edited = edited
  1287         entity.cw_edited = edited
       
  1288         source = self.system_source
  1310         try:
  1289         try:
  1311             only_inline_rels, need_fti_update = True, False
  1290             only_inline_rels, need_fti_update = True, False
  1312             relations = []
  1291             relations = []
  1313             source = self.source_from_eid(entity.eid, session)
       
  1314             for attr in list(edited):
  1292             for attr in list(edited):
  1315                 if attr == 'eid':
  1293                 if attr == 'eid':
  1316                     continue
  1294                     continue
  1317                 rschema = eschema.subjrels[attr]
  1295                 rschema = eschema.subjrels[attr]
  1318                 if rschema.final:
  1296                 if rschema.final:
  1324                     previous_value = entity.related(attr) or None
  1302                     previous_value = entity.related(attr) or None
  1325                     if previous_value is not None:
  1303                     if previous_value is not None:
  1326                         previous_value = previous_value[0][0] # got a result set
  1304                         previous_value = previous_value[0][0] # got a result set
  1327                         if previous_value == entity.cw_attr_cache[attr]:
  1305                         if previous_value == entity.cw_attr_cache[attr]:
  1328                             previous_value = None
  1306                             previous_value = None
  1329                         elif source.should_call_hooks:
  1307                         else:
  1330                             hm.call_hooks('before_delete_relation', session,
  1308                             hm.call_hooks('before_delete_relation', session,
  1331                                           eidfrom=entity.eid, rtype=attr,
  1309                                           eidfrom=entity.eid, rtype=attr,
  1332                                           eidto=previous_value)
  1310                                           eidto=previous_value)
  1333                     relations.append((attr, edited[attr], previous_value))
  1311                     relations.append((attr, edited[attr], previous_value))
  1334             if source.should_call_hooks:
  1312             # call hooks for inlined relations
  1335                 # call hooks for inlined relations
  1313             for attr, value, _t in relations:
  1336                 for attr, value, _t in relations:
  1314                 hm.call_hooks('before_add_relation', session,
  1337                     hm.call_hooks('before_add_relation', session,
  1315                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1338                                   eidfrom=entity.eid, rtype=attr, eidto=value)
  1316             if not only_inline_rels:
  1339                 if not only_inline_rels:
  1317                 hm.call_hooks('before_update_entity', session, entity=entity)
  1340                     hm.call_hooks('before_update_entity', session, entity=entity)
       
  1341             if session.is_hook_category_activated('integrity'):
  1318             if session.is_hook_category_activated('integrity'):
  1342                 edited.check()
  1319                 edited.check()
  1343             try:
  1320             try:
  1344                 source.update_entity(session, entity)
  1321                 source.update_entity(session, entity)
  1345                 edited.saved = True
  1322                 edited.saved = True
  1346             except UniqueTogetherError as exc:
  1323             except UniqueTogetherError as exc:
  1347                 userhdlr = session.vreg['adapters'].select(
  1324                 userhdlr = session.vreg['adapters'].select(
  1348                     'IUserFriendlyError', session, entity=entity, exc=exc)
  1325                     'IUserFriendlyError', session, entity=entity, exc=exc)
  1349                 userhdlr.raise_user_exception()
  1326                 userhdlr.raise_user_exception()
  1350             self.system_source.update_info(session, entity, need_fti_update)
  1327             self.system_source.update_info(session, entity, need_fti_update)
  1351             if source.should_call_hooks:
  1328             if not only_inline_rels:
  1352                 if not only_inline_rels:
  1329                 hm.call_hooks('after_update_entity', session, entity=entity)
  1353                     hm.call_hooks('after_update_entity', session, entity=entity)
  1330             for attr, value, prevvalue in relations:
  1354                 for attr, value, prevvalue in relations:
  1331                 # if the relation is already cached, update existant cache
  1355                     # if the relation is already cached, update existant cache
  1332                 relcache = entity.cw_relation_cached(attr, 'subject')
  1356                     relcache = entity.cw_relation_cached(attr, 'subject')
  1333                 if prevvalue is not None:
  1357                     if prevvalue is not None:
  1334                     hm.call_hooks('after_delete_relation', session,
  1358                         hm.call_hooks('after_delete_relation', session,
  1335                                   eidfrom=entity.eid, rtype=attr, eidto=prevvalue)
  1359                                       eidfrom=entity.eid, rtype=attr, eidto=prevvalue)
       
  1360                         if relcache is not None:
       
  1361                             session.update_rel_cache_del(entity.eid, attr, prevvalue)
       
  1362                     del_existing_rel_if_needed(session, entity.eid, attr, value)
       
  1363                     if relcache is not None:
  1336                     if relcache is not None:
  1364                         session.update_rel_cache_add(entity.eid, attr, value)
  1337                         session.update_rel_cache_del(entity.eid, attr, prevvalue)
  1365                     else:
  1338                 del_existing_rel_if_needed(session, entity.eid, attr, value)
  1366                         entity.cw_set_relation_cache(attr, 'subject',
  1339                 if relcache is not None:
  1367                                                      session.eid_rset(value))
  1340                     session.update_rel_cache_add(entity.eid, attr, value)
  1368                     hm.call_hooks('after_add_relation', session,
  1341                 else:
  1369                                   eidfrom=entity.eid, rtype=attr, eidto=value)
  1342                     entity.cw_set_relation_cache(attr, 'subject',
       
  1343                                                  session.eid_rset(value))
       
  1344                 hm.call_hooks('after_add_relation', session,
       
  1345                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1370         finally:
  1346         finally:
  1371             if orig_edited is not None:
  1347             if orig_edited is not None:
  1372                 entity.cw_edited = orig_edited
  1348                 entity.cw_edited = orig_edited
  1373 
  1349 
  1374 
  1350 
  1382             warn('[3.13] eids should be given as a set', DeprecationWarning,
  1358             warn('[3.13] eids should be given as a set', DeprecationWarning,
  1383                  stacklevel=2)
  1359                  stacklevel=2)
  1384             eids = frozenset(eids)
  1360             eids = frozenset(eids)
  1385         eids = eids - op._container
  1361         eids = eids - op._container
  1386         op._container |= eids
  1362         op._container |= eids
  1387         data_by_etype_source = {} # values are ([list of eids],
  1363         data_by_etype = {} # values are [list of entities]
  1388                                   #             [list of extid],
       
  1389                                   #             [list of entities])
       
  1390         #
  1364         #
  1391         # WARNING: the way this dictionary is populated is heavily optimized
  1365         # WARNING: the way this dictionary is populated is heavily optimized
  1392         # and does not use setdefault on purpose. Unless a new release
  1366         # and does not use setdefault on purpose. Unless a new release
  1393         # of the Python interpreter advertises large perf improvements
  1367         # of the Python interpreter advertises large perf improvements
  1394         # in setdefault, this should not be changed without profiling.
  1368         # in setdefault, this should not be changed without profiling.
  1395 
       
  1396         for eid in eids:
  1369         for eid in eids:
  1397             etype, sourceuri, extid, _ = self.type_and_source_from_eid(eid, session)
  1370             etype = self.type_from_eid(eid, session)
  1398             # XXX should cache entity's cw_metainformation
  1371             # XXX should cache entity's cw_metainformation
  1399             entity = session.entity_from_eid(eid, etype)
  1372             entity = session.entity_from_eid(eid, etype)
  1400             try:
  1373             try:
  1401                 data_by_etype_source[(etype, sourceuri)].append(entity)
  1374                 data_by_etype[etype].append(entity)
  1402             except KeyError:
  1375             except KeyError:
  1403                 data_by_etype_source[(etype, sourceuri)] = [entity]
  1376                 data_by_etype[etype] = [entity]
  1404         for (etype, sourceuri), entities in data_by_etype_source.iteritems():
  1377         source = self.system_source
       
  1378         for etype, entities in data_by_etype.iteritems():
  1405             if server.DEBUG & server.DBG_REPO:
  1379             if server.DEBUG & server.DBG_REPO:
  1406                 print 'DELETE entities', etype, [entity.eid for entity in entities]
  1380                 print 'DELETE entities', etype, [entity.eid for entity in entities]
  1407             source = self.sources_by_uri[sourceuri]
  1381             self.hm.call_hooks('before_delete_entity', session, entities=entities)
  1408             if source.should_call_hooks:
  1382             self._delete_info_multi(session, entities)
  1409                 self.hm.call_hooks('before_delete_entity', session, entities=entities)
       
  1410             if session.deleted_in_transaction(source.eid):
       
  1411                 # source is being deleted, think to give scleanup argument
       
  1412                 self._delete_info_multi(session, entities, scleanup=source.eid)
       
  1413             else:
       
  1414                 self._delete_info_multi(session, entities)
       
  1415             source.delete_entities(session, entities)
  1383             source.delete_entities(session, entities)
  1416             if source.should_call_hooks:
  1384             self.hm.call_hooks('after_delete_entity', session, entities=entities)
  1417                 self.hm.call_hooks('after_delete_entity', session, entities=entities)
       
  1418         # don't clear cache here, it is done in a hook on commit
  1385         # don't clear cache here, it is done in a hook on commit
  1419 
  1386 
  1420     def glob_add_relation(self, session, subject, rtype, object):
  1387     def glob_add_relation(self, session, subject, rtype, object):
  1421         """add a relation to the repository"""
  1388         """add a relation to the repository"""
  1422         self.glob_add_relations(session, {rtype: [(subject, object)]})
  1389         self.glob_add_relations(session, {rtype: [(subject, object)]})
  1424     def glob_add_relations(self, session, relations):
  1391     def glob_add_relations(self, session, relations):
  1425         """add several relations to the repository
  1392         """add several relations to the repository
  1426 
  1393 
  1427         relations is a dictionary rtype: [(subj_eid, obj_eid), ...]
  1394         relations is a dictionary rtype: [(subj_eid, obj_eid), ...]
  1428         """
  1395         """
  1429         sources = {}
  1396         source = self.system_source
       
  1397         relations_by_rtype = {}
  1430         subjects_by_types = {}
  1398         subjects_by_types = {}
  1431         objects_by_types = {}
  1399         objects_by_types = {}
  1432         activintegrity = session.is_hook_category_activated('activeintegrity')
  1400         activintegrity = session.is_hook_category_activated('activeintegrity')
  1433         for rtype, eids_subj_obj in relations.iteritems():
  1401         for rtype, eids_subj_obj in relations.iteritems():
  1434             if server.DEBUG & server.DBG_REPO:
  1402             if server.DEBUG & server.DBG_REPO:
  1435                 for subjeid, objeid in eids_subj_obj:
  1403                 for subjeid, objeid in eids_subj_obj:
  1436                     print 'ADD relation', subjeid, rtype, objeid
  1404                     print 'ADD relation', subjeid, rtype, objeid
  1437             for subjeid, objeid in eids_subj_obj:
  1405             for subjeid, objeid in eids_subj_obj:
  1438                 source = self.locate_relation_source(session, subjeid, rtype, objeid)
       
  1439                 if source not in sources:
       
  1440                     relations_by_rtype = {}
       
  1441                     sources[source] = relations_by_rtype
       
  1442                 else:
       
  1443                     relations_by_rtype = sources[source]
       
  1444                 if rtype in relations_by_rtype:
  1406                 if rtype in relations_by_rtype:
  1445                     relations_by_rtype[rtype].append((subjeid, objeid))
  1407                     relations_by_rtype[rtype].append((subjeid, objeid))
  1446                 else:
  1408                 else:
  1447                     relations_by_rtype[rtype] = [(subjeid, objeid)]
  1409                     relations_by_rtype[rtype] = [(subjeid, objeid)]
  1448                 if not activintegrity:
  1410                 if not activintegrity:
  1472                     if objeid in objects:
  1434                     if objeid in objects:
  1473                         del relations_by_rtype[rtype][objects[objeid]]
  1435                         del relations_by_rtype[rtype][objects[objeid]]
  1474                         objects[objeid] = len(relations_by_rtype[rtype])
  1436                         objects[objeid] = len(relations_by_rtype[rtype])
  1475                         continue
  1437                         continue
  1476                     objects[objeid] = len(relations_by_rtype[rtype])
  1438                     objects[objeid] = len(relations_by_rtype[rtype])
  1477         for source, relations_by_rtype in sources.iteritems():
  1439         for rtype, source_relations in relations_by_rtype.iteritems():
  1478             if source.should_call_hooks:
  1440             self.hm.call_hooks('before_add_relation', session,
  1479                 for rtype, source_relations in relations_by_rtype.iteritems():
  1441                                rtype=rtype, eids_from_to=source_relations)
  1480                     self.hm.call_hooks('before_add_relation', session,
  1442         for rtype, source_relations in relations_by_rtype.iteritems():
  1481                                     rtype=rtype, eids_from_to=source_relations)
  1443             source.add_relations(session, rtype, source_relations)
  1482             for rtype, source_relations in relations_by_rtype.iteritems():
  1444             rschema = self.schema.rschema(rtype)
  1483                 source.add_relations(session, rtype, source_relations)
  1445             for subjeid, objeid in source_relations:
  1484                 rschema = self.schema.rschema(rtype)
  1446                 session.update_rel_cache_add(subjeid, rtype, objeid, rschema.symmetric)
  1485                 for subjeid, objeid in source_relations:
  1447         for rtype, source_relations in relations_by_rtype.iteritems():
  1486                     session.update_rel_cache_add(subjeid, rtype, objeid, rschema.symmetric)
  1448             self.hm.call_hooks('after_add_relation', session,
  1487             if source.should_call_hooks:
  1449                                rtype=rtype, eids_from_to=source_relations)
  1488                 for rtype, source_relations in relations_by_rtype.iteritems():
       
  1489                     self.hm.call_hooks('after_add_relation', session,
       
  1490                                        rtype=rtype, eids_from_to=source_relations)
       
  1491 
  1450 
  1492     def glob_delete_relation(self, session, subject, rtype, object):
  1451     def glob_delete_relation(self, session, subject, rtype, object):
  1493         """delete a relation from the repository"""
  1452         """delete a relation from the repository"""
  1494         if server.DEBUG & server.DBG_REPO:
  1453         if server.DEBUG & server.DBG_REPO:
  1495             print 'DELETE relation', subject, rtype, object
  1454             print 'DELETE relation', subject, rtype, object
  1496         source = self.locate_relation_source(session, subject, rtype, object)
  1455         source = self.system_source
  1497         if source.should_call_hooks:
  1456         self.hm.call_hooks('before_delete_relation', session,
  1498             self.hm.call_hooks('before_delete_relation', session,
  1457                            eidfrom=subject, rtype=rtype, eidto=object)
  1499                                eidfrom=subject, rtype=rtype, eidto=object)
       
  1500         source.delete_relation(session, subject, rtype, object)
  1458         source.delete_relation(session, subject, rtype, object)
  1501         rschema = self.schema.rschema(rtype)
  1459         rschema = self.schema.rschema(rtype)
  1502         session.update_rel_cache_del(subject, rtype, object, rschema.symmetric)
  1460         session.update_rel_cache_del(subject, rtype, object, rschema.symmetric)
  1503         if source.should_call_hooks:
  1461         self.hm.call_hooks('after_delete_relation', session,
  1504             self.hm.call_hooks('after_delete_relation', session,
  1462                            eidfrom=subject, rtype=rtype, eidto=object)
  1505                                eidfrom=subject, rtype=rtype, eidto=object)
       
  1506 
  1463 
  1507 
  1464 
  1508     # pyro handling ###########################################################
  1465     # pyro handling ###########################################################
  1509 
  1466 
  1510     @property
  1467     @property