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 |