264 def map_attribute(self, etype, attr, cb): |
267 def map_attribute(self, etype, attr, cb): |
265 self._rql_sqlgen.attr_map['%s.%s' % (etype, attr)] = cb |
268 self._rql_sqlgen.attr_map['%s.%s' % (etype, attr)] = cb |
266 |
269 |
267 def unmap_attribute(self, etype, attr): |
270 def unmap_attribute(self, etype, attr): |
268 self._rql_sqlgen.attr_map.pop('%s.%s' % (etype, attr), None) |
271 self._rql_sqlgen.attr_map.pop('%s.%s' % (etype, attr), None) |
|
272 |
|
273 def set_storage(self, etype, attr, storage): |
|
274 storage_dict = self._storages.setdefault(etype, {}) |
|
275 storage_dict[attr] = storage |
|
276 self.map_attribute(etype, attr, storage.sqlgen_callback) |
|
277 |
|
278 def unset_storage(self, etype, attr): |
|
279 self._storages[etype].pop(attr) |
|
280 # if etype has no storage left, remove the entry |
|
281 if not self._storages[etype]: |
|
282 del self._storages[etype] |
|
283 self.unmap_attribute(etype, attr) |
269 |
284 |
270 # ISource interface ####################################################### |
285 # ISource interface ####################################################### |
271 |
286 |
272 def compile_rql(self, rql, sols): |
287 def compile_rql(self, rql, sols): |
273 rqlst = self.repo.vreg.rqlhelper.parse(rql) |
288 rqlst = self.repo.vreg.rqlhelper.parse(rql) |
400 try: |
415 try: |
401 del self._temp_table_data[table] |
416 del self._temp_table_data[table] |
402 except KeyError: |
417 except KeyError: |
403 continue |
418 continue |
404 |
419 |
|
420 @contextmanager |
|
421 def _storage_handler(self, entity, event): |
|
422 # 1/ memorize values as they are before the storage is called. |
|
423 # For instance, the BFSStorage will replace the `data` |
|
424 # binary value with a Binary containing the destination path |
|
425 # on the filesystem. To make the entity.data usage absolutely |
|
426 # transparent, we'll have to reset entity.data to its binary |
|
427 # value once the SQL query will be executed |
|
428 orig_values = {} |
|
429 etype = entity.__regid__ |
|
430 for attr, storage in self._storages.get(etype, {}).items(): |
|
431 if attr in entity.edited_attributes: |
|
432 orig_values[attr] = entity[attr] |
|
433 handler = getattr(storage, 'entity_%s' % event) |
|
434 handler(entity, attr) |
|
435 yield # 2/ execute the source's instructions |
|
436 # 3/ restore original values |
|
437 for attr, value in orig_values.items(): |
|
438 entity[attr] = value |
|
439 |
405 def add_entity(self, session, entity): |
440 def add_entity(self, session, entity): |
406 """add a new entity to the source""" |
441 """add a new entity to the source""" |
407 attrs = self.preprocess_entity(entity) |
442 with self._storage_handler(entity, 'added'): |
408 sql = self.sqlgen.insert(SQL_PREFIX + entity.__regid__, attrs) |
443 attrs = self.preprocess_entity(entity) |
409 self.doexec(session, sql, attrs) |
444 sql = self.sqlgen.insert(SQL_PREFIX + entity.__regid__, attrs) |
410 if session.undoable_action('C', entity.__regid__): |
445 self.doexec(session, sql, attrs) |
411 self._record_tx_action(session, 'tx_entity_actions', 'C', |
446 if session.undoable_action('C', entity.__regid__): |
412 etype=entity.__regid__, eid=entity.eid) |
447 self._record_tx_action(session, 'tx_entity_actions', 'C', |
|
448 etype=entity.__regid__, eid=entity.eid) |
413 |
449 |
414 def update_entity(self, session, entity): |
450 def update_entity(self, session, entity): |
415 """replace an entity in the source""" |
451 """replace an entity in the source""" |
416 attrs = self.preprocess_entity(entity) |
452 with self._storage_handler(entity, 'updated'): |
417 if session.undoable_action('U', entity.__regid__): |
453 attrs = self.preprocess_entity(entity) |
418 changes = self._save_attrs(session, entity, attrs) |
454 if session.undoable_action('U', entity.__regid__): |
419 self._record_tx_action(session, 'tx_entity_actions', 'U', |
455 changes = self._save_attrs(session, entity, attrs) |
420 etype=entity.__regid__, eid=entity.eid, |
456 self._record_tx_action(session, 'tx_entity_actions', 'U', |
421 changes=self._binary(dumps(changes))) |
457 etype=entity.__regid__, eid=entity.eid, |
422 sql = self.sqlgen.update(SQL_PREFIX + entity.__regid__, attrs, |
458 changes=self._binary(dumps(changes))) |
423 ['cw_eid']) |
459 sql = self.sqlgen.update(SQL_PREFIX + entity.__regid__, attrs, |
424 self.doexec(session, sql, attrs) |
460 ['cw_eid']) |
|
461 self.doexec(session, sql, attrs) |
425 |
462 |
426 def delete_entity(self, session, entity): |
463 def delete_entity(self, session, entity): |
427 """delete an entity from the source""" |
464 """delete an entity from the source""" |
428 if session.undoable_action('D', entity.__regid__): |
465 with self._storage_handler(entity, 'deleted'): |
429 attrs = [SQL_PREFIX + r.type |
466 if session.undoable_action('D', entity.__regid__): |
430 for r in entity.e_schema.subject_relations() |
467 attrs = [SQL_PREFIX + r.type |
431 if (r.final or r.inlined) and not r in VIRTUAL_RTYPES] |
468 for r in entity.e_schema.subject_relations() |
432 changes = self._save_attrs(session, entity, attrs) |
469 if (r.final or r.inlined) and not r in VIRTUAL_RTYPES] |
433 self._record_tx_action(session, 'tx_entity_actions', 'D', |
470 changes = self._save_attrs(session, entity, attrs) |
434 etype=entity.__regid__, eid=entity.eid, |
471 self._record_tx_action(session, 'tx_entity_actions', 'D', |
435 changes=self._binary(dumps(changes))) |
472 etype=entity.__regid__, eid=entity.eid, |
436 attrs = {'cw_eid': entity.eid} |
473 changes=self._binary(dumps(changes))) |
437 sql = self.sqlgen.delete(SQL_PREFIX + entity.__regid__, attrs) |
474 attrs = {'cw_eid': entity.eid} |
438 self.doexec(session, sql, attrs) |
475 sql = self.sqlgen.delete(SQL_PREFIX + entity.__regid__, attrs) |
|
476 self.doexec(session, sql, attrs) |
439 |
477 |
440 def _add_relation(self, session, subject, rtype, object, inlined=False): |
478 def _add_relation(self, session, subject, rtype, object, inlined=False): |
441 """add a relation to the source""" |
479 """add a relation to the source""" |
442 if inlined is False: |
480 if inlined is False: |
443 attrs = {'eid_from': subject, 'eid_to': object} |
481 attrs = {'eid_from': subject, 'eid_to': object} |