server/repository.py
changeset 9609 e7d38148799e
parent 9607 6942622fd5dc
child 9622 637a12b0d3a2
equal deleted inserted replaced
9608:e4d9a489ec3f 9609:e7d38148799e
  1189                         raise
  1189                         raise
  1190                     self.exception('error while cascading delete for entity %s. RQL: %s',
  1190                     self.exception('error while cascading delete for entity %s. RQL: %s',
  1191                                    entities, rql)
  1191                                    entities, rql)
  1192         self.system_source.delete_info_multi(session, entities)
  1192         self.system_source.delete_info_multi(session, entities)
  1193 
  1193 
  1194     def init_entity_caches(self, session, entity, source):
  1194     def init_entity_caches(self, cnx, entity, source):
  1195         """add entity to session entities cache and repo's extid cache.
  1195         """add entity to connection entities cache and repo's extid cache.
  1196         Return entity's ext id if the source isn't the system source.
  1196         Return entity's ext id if the source isn't the system source.
  1197         """
  1197         """
  1198         session.set_entity_cache(entity)
  1198         cnx.set_entity_cache(entity)
  1199         if source.uri == 'system':
  1199         if source.uri == 'system':
  1200             extid = None
  1200             extid = None
  1201         else:
  1201         else:
  1202             extid = source.get_extid(entity)
  1202             extid = source.get_extid(entity)
  1203             self._extid_cache[str(extid)] = entity.eid
  1203             self._extid_cache[str(extid)] = entity.eid
  1204         self._type_source_cache[entity.eid] = (entity.cw_etype, extid, source.uri)
  1204         self._type_source_cache[entity.eid] = (entity.cw_etype, extid, source.uri)
  1205         return extid
  1205         return extid
  1206 
  1206 
  1207     def glob_add_entity(self, session, edited):
  1207     def glob_add_entity(self, cnx, edited):
  1208         """add an entity to the repository
  1208         """add an entity to the repository
  1209 
  1209 
  1210         the entity eid should originaly be None and a unique eid is assigned to
  1210         the entity eid should originaly be None and a unique eid is assigned to
  1211         the entity instance
  1211         the entity instance
  1212         """
  1212         """
  1214         entity._cw_is_saved = False # entity has an eid but is not yet saved
  1214         entity._cw_is_saved = False # entity has an eid but is not yet saved
  1215         # init edited_attributes before calling before_add_entity hooks
  1215         # init edited_attributes before calling before_add_entity hooks
  1216         entity.cw_edited = edited
  1216         entity.cw_edited = edited
  1217         source = self.system_source
  1217         source = self.system_source
  1218         # allocate an eid to the entity before calling hooks
  1218         # allocate an eid to the entity before calling hooks
  1219         entity.eid = self.system_source.create_eid(session)
  1219         entity.eid = self.system_source.create_eid(cnx)
  1220         # set caches asap
  1220         # set caches asap
  1221         extid = self.init_entity_caches(session, entity, source)
  1221         extid = self.init_entity_caches(cnx, entity, source)
  1222         if server.DEBUG & server.DBG_REPO:
  1222         if server.DEBUG & server.DBG_REPO:
  1223             print 'ADD entity', self, entity.cw_etype, entity.eid, edited
  1223             print 'ADD entity', self, entity.cw_etype, entity.eid, edited
  1224         prefill_entity_caches(entity)
  1224         prefill_entity_caches(entity)
  1225         self.hm.call_hooks('before_add_entity', session, entity=entity)
  1225         self.hm.call_hooks('before_add_entity', cnx, entity=entity)
  1226         relations = preprocess_inlined_relations(session, entity)
  1226         relations = preprocess_inlined_relations(cnx, entity)
  1227         edited.set_defaults()
  1227         edited.set_defaults()
  1228         if session.is_hook_category_activated('integrity'):
  1228         if cnx.is_hook_category_activated('integrity'):
  1229             edited.check(creation=True)
  1229             edited.check(creation=True)
  1230         try:
  1230         try:
  1231             source.add_entity(session, entity)
  1231             source.add_entity(cnx, entity)
  1232         except UniqueTogetherError as exc:
  1232         except UniqueTogetherError as exc:
  1233             userhdlr = session.vreg['adapters'].select(
  1233             userhdlr = cnx.vreg['adapters'].select(
  1234                 'IUserFriendlyError', session, entity=entity, exc=exc)
  1234                 'IUserFriendlyError', cnx, entity=entity, exc=exc)
  1235             userhdlr.raise_user_exception()
  1235             userhdlr.raise_user_exception()
  1236         self.add_info(session, entity, source, extid, complete=False)
  1236         self.add_info(cnx, entity, source, extid, complete=False)
  1237         edited.saved = entity._cw_is_saved = True
  1237         edited.saved = entity._cw_is_saved = True
  1238         # trigger after_add_entity after after_add_relation
  1238         # trigger after_add_entity after after_add_relation
  1239         self.hm.call_hooks('after_add_entity', session, entity=entity)
  1239         self.hm.call_hooks('after_add_entity', cnx, entity=entity)
  1240         # call hooks for inlined relations
  1240         # call hooks for inlined relations
  1241         for attr, value in relations:
  1241         for attr, value in relations:
  1242             self.hm.call_hooks('before_add_relation', session,
  1242             self.hm.call_hooks('before_add_relation', cnx,
  1243                                 eidfrom=entity.eid, rtype=attr, eidto=value)
  1243                                 eidfrom=entity.eid, rtype=attr, eidto=value)
  1244             self.hm.call_hooks('after_add_relation', session,
  1244             self.hm.call_hooks('after_add_relation', cnx,
  1245                                 eidfrom=entity.eid, rtype=attr, eidto=value)
  1245                                 eidfrom=entity.eid, rtype=attr, eidto=value)
  1246         return entity.eid
  1246         return entity.eid
  1247 
  1247 
  1248     def glob_update_entity(self, session, edited):
  1248     def glob_update_entity(self, cnx, edited):
  1249         """replace an entity in the repository
  1249         """replace an entity in the repository
  1250         the type and the eid of an entity must not be changed
  1250         the type and the eid of an entity must not be changed
  1251         """
  1251         """
  1252         entity = edited.entity
  1252         entity = edited.entity
  1253         if server.DEBUG & server.DBG_REPO:
  1253         if server.DEBUG & server.DBG_REPO:
  1254             print 'UPDATE entity', entity.cw_etype, entity.eid, \
  1254             print 'UPDATE entity', entity.cw_etype, entity.eid, \
  1255                   entity.cw_attr_cache, edited
  1255                   entity.cw_attr_cache, edited
  1256         hm = self.hm
  1256         hm = self.hm
  1257         eschema = entity.e_schema
  1257         eschema = entity.e_schema
  1258         session.set_entity_cache(entity)
  1258         cnx.set_entity_cache(entity)
  1259         orig_edited = getattr(entity, 'cw_edited', None)
  1259         orig_edited = getattr(entity, 'cw_edited', None)
  1260         entity.cw_edited = edited
  1260         entity.cw_edited = edited
  1261         source = self.system_source
  1261         source = self.system_source
  1262         try:
  1262         try:
  1263             only_inline_rels, need_fti_update = True, False
  1263             only_inline_rels, need_fti_update = True, False
  1276                     if previous_value is not None:
  1276                     if previous_value is not None:
  1277                         previous_value = previous_value[0][0] # got a result set
  1277                         previous_value = previous_value[0][0] # got a result set
  1278                         if previous_value == entity.cw_attr_cache[attr]:
  1278                         if previous_value == entity.cw_attr_cache[attr]:
  1279                             previous_value = None
  1279                             previous_value = None
  1280                         else:
  1280                         else:
  1281                             hm.call_hooks('before_delete_relation', session,
  1281                             hm.call_hooks('before_delete_relation', cnx,
  1282                                           eidfrom=entity.eid, rtype=attr,
  1282                                           eidfrom=entity.eid, rtype=attr,
  1283                                           eidto=previous_value)
  1283                                           eidto=previous_value)
  1284                     relations.append((attr, edited[attr], previous_value))
  1284                     relations.append((attr, edited[attr], previous_value))
  1285             # call hooks for inlined relations
  1285             # call hooks for inlined relations
  1286             for attr, value, _t in relations:
  1286             for attr, value, _t in relations:
  1287                 hm.call_hooks('before_add_relation', session,
  1287                 hm.call_hooks('before_add_relation', cnx,
  1288                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1288                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1289             if not only_inline_rels:
  1289             if not only_inline_rels:
  1290                 hm.call_hooks('before_update_entity', session, entity=entity)
  1290                 hm.call_hooks('before_update_entity', cnx, entity=entity)
  1291             if session.is_hook_category_activated('integrity'):
  1291             if cnx.is_hook_category_activated('integrity'):
  1292                 edited.check()
  1292                 edited.check()
  1293             try:
  1293             try:
  1294                 source.update_entity(session, entity)
  1294                 source.update_entity(cnx, entity)
  1295                 edited.saved = True
  1295                 edited.saved = True
  1296             except UniqueTogetherError as exc:
  1296             except UniqueTogetherError as exc:
  1297                 userhdlr = session.vreg['adapters'].select(
  1297                 userhdlr = cnx.vreg['adapters'].select(
  1298                     'IUserFriendlyError', session, entity=entity, exc=exc)
  1298                     'IUserFriendlyError', cnx, entity=entity, exc=exc)
  1299                 userhdlr.raise_user_exception()
  1299                 userhdlr.raise_user_exception()
  1300             self.system_source.update_info(session, entity, need_fti_update)
  1300             self.system_source.update_info(cnx, entity, need_fti_update)
  1301             if not only_inline_rels:
  1301             if not only_inline_rels:
  1302                 hm.call_hooks('after_update_entity', session, entity=entity)
  1302                 hm.call_hooks('after_update_entity', cnx, entity=entity)
  1303             for attr, value, prevvalue in relations:
  1303             for attr, value, prevvalue in relations:
  1304                 # if the relation is already cached, update existant cache
  1304                 # if the relation is already cached, update existant cache
  1305                 relcache = entity.cw_relation_cached(attr, 'subject')
  1305                 relcache = entity.cw_relation_cached(attr, 'subject')
  1306                 if prevvalue is not None:
  1306                 if prevvalue is not None:
  1307                     hm.call_hooks('after_delete_relation', session,
  1307                     hm.call_hooks('after_delete_relation', cnx,
  1308                                   eidfrom=entity.eid, rtype=attr, eidto=prevvalue)
  1308                                   eidfrom=entity.eid, rtype=attr, eidto=prevvalue)
  1309                     if relcache is not None:
  1309                     if relcache is not None:
  1310                         session.update_rel_cache_del(entity.eid, attr, prevvalue)
  1310                         cnx.update_rel_cache_del(entity.eid, attr, prevvalue)
  1311                 del_existing_rel_if_needed(session, entity.eid, attr, value)
  1311                 del_existing_rel_if_needed(cnx, entity.eid, attr, value)
  1312                 if relcache is not None:
  1312                 if relcache is not None:
  1313                     session.update_rel_cache_add(entity.eid, attr, value)
  1313                     cnx.update_rel_cache_add(entity.eid, attr, value)
  1314                 else:
  1314                 else:
  1315                     entity.cw_set_relation_cache(attr, 'subject',
  1315                     entity.cw_set_relation_cache(attr, 'subject',
  1316                                                  session.eid_rset(value))
  1316                                                  cnx.eid_rset(value))
  1317                 hm.call_hooks('after_add_relation', session,
  1317                 hm.call_hooks('after_add_relation', cnx,
  1318                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1318                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1319         finally:
  1319         finally:
  1320             if orig_edited is not None:
  1320             if orig_edited is not None:
  1321                 entity.cw_edited = orig_edited
  1321                 entity.cw_edited = orig_edited
  1322 
  1322 
  1323 
  1323 
  1324     def glob_delete_entities(self, session, eids):
  1324     def glob_delete_entities(self, cnx, eids):
  1325         """delete a list of  entities and all related entities from the repository"""
  1325         """delete a list of  entities and all related entities from the repository"""
  1326         # mark eids as being deleted in session info and setup cache update
  1326         # mark eids as being deleted in cnx info and setup cache update
  1327         # operation (register pending eids before actual deletion to avoid
  1327         # operation (register pending eids before actual deletion to avoid
  1328         # multiple call to glob_delete_entities)
  1328         # multiple call to glob_delete_entities)
  1329         op = hook.CleanupDeletedEidsCacheOp.get_instance(session)
  1329         op = hook.CleanupDeletedEidsCacheOp.get_instance(cnx)
  1330         if not isinstance(eids, (set, frozenset)):
  1330         if not isinstance(eids, (set, frozenset)):
  1331             warn('[3.13] eids should be given as a set', DeprecationWarning,
  1331             warn('[3.13] eids should be given as a set', DeprecationWarning,
  1332                  stacklevel=2)
  1332                  stacklevel=2)
  1333             eids = frozenset(eids)
  1333             eids = frozenset(eids)
  1334         eids = eids - op._container
  1334         eids = eids - op._container
  1338         # WARNING: the way this dictionary is populated is heavily optimized
  1338         # WARNING: the way this dictionary is populated is heavily optimized
  1339         # and does not use setdefault on purpose. Unless a new release
  1339         # and does not use setdefault on purpose. Unless a new release
  1340         # of the Python interpreter advertises large perf improvements
  1340         # of the Python interpreter advertises large perf improvements
  1341         # in setdefault, this should not be changed without profiling.
  1341         # in setdefault, this should not be changed without profiling.
  1342         for eid in eids:
  1342         for eid in eids:
  1343             etype = self.type_from_eid(eid, session)
  1343             etype = self.type_from_eid(eid, cnx)
  1344             # XXX should cache entity's cw_metainformation
  1344             # XXX should cache entity's cw_metainformation
  1345             entity = session.entity_from_eid(eid, etype)
  1345             entity = cnx.entity_from_eid(eid, etype)
  1346             try:
  1346             try:
  1347                 data_by_etype[etype].append(entity)
  1347                 data_by_etype[etype].append(entity)
  1348             except KeyError:
  1348             except KeyError:
  1349                 data_by_etype[etype] = [entity]
  1349                 data_by_etype[etype] = [entity]
  1350         source = self.system_source
  1350         source = self.system_source
  1351         for etype, entities in data_by_etype.iteritems():
  1351         for etype, entities in data_by_etype.iteritems():
  1352             if server.DEBUG & server.DBG_REPO:
  1352             if server.DEBUG & server.DBG_REPO:
  1353                 print 'DELETE entities', etype, [entity.eid for entity in entities]
  1353                 print 'DELETE entities', etype, [entity.eid for entity in entities]
  1354             self.hm.call_hooks('before_delete_entity', session, entities=entities)
  1354             self.hm.call_hooks('before_delete_entity', cnx, entities=entities)
  1355             self._delete_info_multi(session, entities)
  1355             self._delete_info_multi(cnx, entities)
  1356             source.delete_entities(session, entities)
  1356             source.delete_entities(cnx, entities)
  1357             self.hm.call_hooks('after_delete_entity', session, entities=entities)
  1357             self.hm.call_hooks('after_delete_entity', cnx, entities=entities)
  1358         # don't clear cache here, it is done in a hook on commit
  1358         # don't clear cache here, it is done in a hook on commit
  1359 
  1359 
  1360     def glob_add_relation(self, session, subject, rtype, object):
  1360     def glob_add_relation(self, cnx, subject, rtype, object):
  1361         """add a relation to the repository"""
  1361         """add a relation to the repository"""
  1362         self.glob_add_relations(session, {rtype: [(subject, object)]})
  1362         self.glob_add_relations(cnx, {rtype: [(subject, object)]})
  1363 
  1363 
  1364     def glob_add_relations(self, session, relations):
  1364     def glob_add_relations(self, cnx, relations):
  1365         """add several relations to the repository
  1365         """add several relations to the repository
  1366 
  1366 
  1367         relations is a dictionary rtype: [(subj_eid, obj_eid), ...]
  1367         relations is a dictionary rtype: [(subj_eid, obj_eid), ...]
  1368         """
  1368         """
  1369         source = self.system_source
  1369         source = self.system_source
  1370         relations_by_rtype = {}
  1370         relations_by_rtype = {}
  1371         subjects_by_types = {}
  1371         subjects_by_types = {}
  1372         objects_by_types = {}
  1372         objects_by_types = {}
  1373         activintegrity = session.is_hook_category_activated('activeintegrity')
  1373         activintegrity = cnx.is_hook_category_activated('activeintegrity')
  1374         for rtype, eids_subj_obj in relations.iteritems():
  1374         for rtype, eids_subj_obj in relations.iteritems():
  1375             if server.DEBUG & server.DBG_REPO:
  1375             if server.DEBUG & server.DBG_REPO:
  1376                 for subjeid, objeid in eids_subj_obj:
  1376                 for subjeid, objeid in eids_subj_obj:
  1377                     print 'ADD relation', subjeid, rtype, objeid
  1377                     print 'ADD relation', subjeid, rtype, objeid
  1378             for subjeid, objeid in eids_subj_obj:
  1378             for subjeid, objeid in eids_subj_obj:
  1383                 if not activintegrity:
  1383                 if not activintegrity:
  1384                     continue
  1384                     continue
  1385                 # take care to relation of cardinality '?1', as all eids will
  1385                 # take care to relation of cardinality '?1', as all eids will
  1386                 # be inserted later, we've remove duplicated eids since they
  1386                 # be inserted later, we've remove duplicated eids since they
  1387                 # won't be catched by `del_existing_rel_if_needed`
  1387                 # won't be catched by `del_existing_rel_if_needed`
  1388                 rdef = session.rtype_eids_rdef(rtype, subjeid, objeid)
  1388                 rdef = cnx.rtype_eids_rdef(rtype, subjeid, objeid)
  1389                 card = rdef.cardinality
  1389                 card = rdef.cardinality
  1390                 if card[0] in '?1':
  1390                 if card[0] in '?1':
  1391                     with session.security_enabled(read=False):
  1391                     with cnx.security_enabled(read=False):
  1392                         session.execute('DELETE X %s Y WHERE X eid %%(x)s, '
  1392                         cnx.execute('DELETE X %s Y WHERE X eid %%(x)s, '
  1393                                         'NOT Y eid %%(y)s' % rtype,
  1393                                     'NOT Y eid %%(y)s' % rtype,
  1394                                         {'x': subjeid, 'y': objeid})
  1394                                     {'x': subjeid, 'y': objeid})
  1395                     subjects = subjects_by_types.setdefault(rdef, {})
  1395                     subjects = subjects_by_types.setdefault(rdef, {})
  1396                     if subjeid in subjects:
  1396                     if subjeid in subjects:
  1397                         del relations_by_rtype[rtype][subjects[subjeid]]
  1397                         del relations_by_rtype[rtype][subjects[subjeid]]
  1398                         subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
  1398                         subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
  1399                         continue
  1399                         continue
  1400                     subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
  1400                     subjects[subjeid] = len(relations_by_rtype[rtype]) - 1
  1401                 if card[1] in '?1':
  1401                 if card[1] in '?1':
  1402                     with session.security_enabled(read=False):
  1402                     with cnx.security_enabled(read=False):
  1403                         session.execute('DELETE X %s Y WHERE Y eid %%(y)s, '
  1403                         cnx.execute('DELETE X %s Y WHERE Y eid %%(y)s, '
  1404                                         'NOT X eid %%(x)s' % rtype,
  1404                                     'NOT X eid %%(x)s' % rtype,
  1405                                         {'x': subjeid, 'y': objeid})
  1405                                     {'x': subjeid, 'y': objeid})
  1406                     objects = objects_by_types.setdefault(rdef, {})
  1406                     objects = objects_by_types.setdefault(rdef, {})
  1407                     if objeid in objects:
  1407                     if objeid in objects:
  1408                         del relations_by_rtype[rtype][objects[objeid]]
  1408                         del relations_by_rtype[rtype][objects[objeid]]
  1409                         objects[objeid] = len(relations_by_rtype[rtype])
  1409                         objects[objeid] = len(relations_by_rtype[rtype])
  1410                         continue
  1410                         continue
  1411                     objects[objeid] = len(relations_by_rtype[rtype])
  1411                     objects[objeid] = len(relations_by_rtype[rtype])
  1412         for rtype, source_relations in relations_by_rtype.iteritems():
  1412         for rtype, source_relations in relations_by_rtype.iteritems():
  1413             self.hm.call_hooks('before_add_relation', session,
  1413             self.hm.call_hooks('before_add_relation', cnx,
  1414                                rtype=rtype, eids_from_to=source_relations)
  1414                                rtype=rtype, eids_from_to=source_relations)
  1415         for rtype, source_relations in relations_by_rtype.iteritems():
  1415         for rtype, source_relations in relations_by_rtype.iteritems():
  1416             source.add_relations(session, rtype, source_relations)
  1416             source.add_relations(cnx, rtype, source_relations)
  1417             rschema = self.schema.rschema(rtype)
  1417             rschema = self.schema.rschema(rtype)
  1418             for subjeid, objeid in source_relations:
  1418             for subjeid, objeid in source_relations:
  1419                 session.update_rel_cache_add(subjeid, rtype, objeid, rschema.symmetric)
  1419                 cnx.update_rel_cache_add(subjeid, rtype, objeid, rschema.symmetric)
  1420         for rtype, source_relations in relations_by_rtype.iteritems():
  1420         for rtype, source_relations in relations_by_rtype.iteritems():
  1421             self.hm.call_hooks('after_add_relation', session,
  1421             self.hm.call_hooks('after_add_relation', cnx,
  1422                                rtype=rtype, eids_from_to=source_relations)
  1422                                rtype=rtype, eids_from_to=source_relations)
  1423 
  1423 
  1424     def glob_delete_relation(self, session, subject, rtype, object):
  1424     def glob_delete_relation(self, cnx, subject, rtype, object):
  1425         """delete a relation from the repository"""
  1425         """delete a relation from the repository"""
  1426         if server.DEBUG & server.DBG_REPO:
  1426         if server.DEBUG & server.DBG_REPO:
  1427             print 'DELETE relation', subject, rtype, object
  1427             print 'DELETE relation', subject, rtype, object
  1428         source = self.system_source
  1428         source = self.system_source
  1429         self.hm.call_hooks('before_delete_relation', session,
  1429         self.hm.call_hooks('before_delete_relation', cnx,
  1430                            eidfrom=subject, rtype=rtype, eidto=object)
  1430                            eidfrom=subject, rtype=rtype, eidto=object)
  1431         source.delete_relation(session, subject, rtype, object)
  1431         source.delete_relation(cnx, subject, rtype, object)
  1432         rschema = self.schema.rschema(rtype)
  1432         rschema = self.schema.rschema(rtype)
  1433         session.update_rel_cache_del(subject, rtype, object, rschema.symmetric)
  1433         cnx.update_rel_cache_del(subject, rtype, object, rschema.symmetric)
  1434         self.hm.call_hooks('after_delete_relation', session,
  1434         self.hm.call_hooks('after_delete_relation', cnx,
  1435                            eidfrom=subject, rtype=rtype, eidto=object)
  1435                            eidfrom=subject, rtype=rtype, eidto=object)
  1436 
  1436 
  1437 
  1437 
  1438     # pyro handling ###########################################################
  1438     # pyro handling ###########################################################
  1439 
  1439