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 |
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 |