server/repository.py
changeset 4913 083b4d454192
parent 4899 c666d265fb95
child 4951 7dc54e12c606
equal deleted inserted replaced
4912:9767cc516b4f 4913:083b4d454192
    22 import sys
    22 import sys
    23 import Queue
    23 import Queue
    24 from os.path import join
    24 from os.path import join
    25 from datetime import datetime
    25 from datetime import datetime
    26 from time import time, localtime, strftime
    26 from time import time, localtime, strftime
       
    27 #from pickle import dumps
    27 
    28 
    28 from logilab.common.decorators import cached
    29 from logilab.common.decorators import cached
    29 from logilab.common.compat import any
    30 from logilab.common.compat import any
       
    31 from logilab.common import flatten
    30 
    32 
    31 from yams import BadSchemaDefinition
    33 from yams import BadSchemaDefinition
    32 from rql import RQLSyntaxError
    34 from rql import RQLSyntaxError
    33 
    35 
    34 from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP,
    36 from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP,
   628 
   630 
   629     def commit(self, sessionid):
   631     def commit(self, sessionid):
   630         """commit transaction for the session with the given id"""
   632         """commit transaction for the session with the given id"""
   631         self.debug('begin commit for session %s', sessionid)
   633         self.debug('begin commit for session %s', sessionid)
   632         try:
   634         try:
   633             self._get_session(sessionid).commit()
   635             return self._get_session(sessionid).commit()
   634         except (ValidationError, Unauthorized):
   636         except (ValidationError, Unauthorized):
   635             raise
   637             raise
   636         except:
   638         except:
   637             self.exception('unexpected error')
   639             self.exception('unexpected error')
   638             raise
   640             raise
   677         * check session id validity
   679         * check session id validity
   678         * update user information on each user's request (i.e. groups and
   680         * update user information on each user's request (i.e. groups and
   679           custom properties)
   681           custom properties)
   680         """
   682         """
   681         session = self._get_session(sessionid, setpool=False)
   683         session = self._get_session(sessionid, setpool=False)
   682         # update session properties
       
   683         for prop, value in props.items():
   684         for prop, value in props.items():
   684             session.change_property(prop, value)
   685             session.change_property(prop, value)
       
   686 
       
   687     def undoable_transactions(self, sessionid, ueid=None, **actionfilters):
       
   688         """See :class:`cubicweb.dbapi.Connection.undoable_transactions`"""
       
   689         session = self._get_session(sessionid, setpool=True)
       
   690         try:
       
   691             return self.system_source.undoable_transactions(session, ueid,
       
   692                                                             **actionfilters)
       
   693         finally:
       
   694             session.reset_pool()
       
   695 
       
   696     def transaction_info(self, sessionid, txuuid):
       
   697         """See :class:`cubicweb.dbapi.Connection.transaction_info`"""
       
   698         session = self._get_session(sessionid, setpool=True)
       
   699         try:
       
   700             return self.system_source.tx_info(session, txuuid)
       
   701         finally:
       
   702             session.reset_pool()
       
   703 
       
   704     def transaction_actions(self, sessionid, txuuid, public=True):
       
   705         """See :class:`cubicweb.dbapi.Connection.transaction_actions`"""
       
   706         session = self._get_session(sessionid, setpool=True)
       
   707         try:
       
   708             return self.system_source.tx_actions(session, txuuid, public)
       
   709         finally:
       
   710             session.reset_pool()
       
   711 
       
   712     def undo_transaction(self, sessionid, txuuid):
       
   713         """See :class:`cubicweb.dbapi.Connection.undo_transaction`"""
       
   714         session = self._get_session(sessionid, setpool=True)
       
   715         try:
       
   716             return self.system_source.undo_transaction(session, txuuid)
       
   717         finally:
       
   718             session.reset_pool()
   685 
   719 
   686     # public (inter-repository) interface #####################################
   720     # public (inter-repository) interface #####################################
   687 
   721 
   688     def entities_modified_since(self, etypes, mtime):
   722     def entities_modified_since(self, etypes, mtime):
   689         """function designed to be called from an external repository which
   723         """function designed to be called from an external repository which
   884         new = session.transaction_data.setdefault('neweids', set())
   918         new = session.transaction_data.setdefault('neweids', set())
   885         new.add(entity.eid)
   919         new.add(entity.eid)
   886         self.system_source.add_info(session, entity, source, extid, complete)
   920         self.system_source.add_info(session, entity, source, extid, complete)
   887         CleanupEidTypeCacheOp(session)
   921         CleanupEidTypeCacheOp(session)
   888 
   922 
   889     def delete_info(self, session, eid):
   923     def delete_info(self, session, entity, sourceuri, extid):
   890         self._prepare_delete_info(session, eid)
   924         """called by external source when some entity known by the system source
   891         self._delete_info(session, eid)
   925         has been deleted in the external source
   892 
   926         """
   893     def _prepare_delete_info(self, session, eid):
   927         self._prepare_delete_info(session, entity, sourceuri)
       
   928         self._delete_info(session, entity, sourceuri, extid)
       
   929 
       
   930     def _prepare_delete_info(self, session, entity, sourceuri):
   894         """prepare the repository for deletion of an entity:
   931         """prepare the repository for deletion of an entity:
   895         * update the fti
   932         * update the fti
   896         * mark eid as being deleted in session info
   933         * mark eid as being deleted in session info
   897         * setup cache update operation
   934         * setup cache update operation
   898         """
   935         * if undoable, get back all entity's attributes and relation
       
   936         """
       
   937         eid = entity.eid
   899         self.system_source.fti_unindex_entity(session, eid)
   938         self.system_source.fti_unindex_entity(session, eid)
   900         pending = session.transaction_data.setdefault('pendingeids', set())
   939         pending = session.transaction_data.setdefault('pendingeids', set())
   901         pending.add(eid)
   940         pending.add(eid)
   902         CleanupEidTypeCacheOp(session)
   941         CleanupEidTypeCacheOp(session)
   903 
   942 
   904     def _delete_info(self, session, eid):
   943     def _delete_info(self, session, entity, sourceuri, extid):
       
   944                      # attributes=None, relations=None):
   905         """delete system information on deletion of an entity:
   945         """delete system information on deletion of an entity:
   906         * delete all relations on this entity
   946         * delete all remaining relations from/to this entity
   907         * transfer record from the entities table to the deleted_entities table
   947         * call delete info on the system source which will transfer record from
   908         """
   948           the entities table to the deleted_entities table
   909         etype, uri, extid = self.type_and_source_from_eid(eid, session)
   949         """
   910         self._clear_eid_relations(session, etype, eid)
       
   911         self.system_source.delete_info(session, eid, etype, uri, extid)
       
   912 
       
   913     def _clear_eid_relations(self, session, etype, eid):
       
   914         """when a entity is deleted, build and execute rql query to delete all
       
   915         its relations
       
   916         """
       
   917         rql = []
       
   918         eschema = self.schema.eschema(etype)
       
   919         pendingrtypes = session.transaction_data.get('pendingrtypes', ())
   950         pendingrtypes = session.transaction_data.get('pendingrtypes', ())
       
   951         # delete remaining relations: if user can delete the entity, he can
       
   952         # delete all its relations without security checking
   920         with security_enabled(session, read=False, write=False):
   953         with security_enabled(session, read=False, write=False):
   921             for rschema, targetschemas, x in eschema.relation_definitions():
   954             eid = entity.eid
       
   955             for rschema, _, role in entity.e_schema.relation_definitions():
   922                 rtype = rschema.type
   956                 rtype = rschema.type
   923                 if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes:
   957                 if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes:
   924                     continue
   958                     continue
   925                 var = '%s%s' % (rtype.upper(), x.upper())
   959                 if role == 'subject':
   926                 if x == 'subject':
       
   927                     # don't skip inlined relation so they are regularly
   960                     # don't skip inlined relation so they are regularly
   928                     # deleted and so hooks are correctly called
   961                     # deleted and so hooks are correctly called
   929                     selection = 'X %s %s' % (rtype, var)
   962                     selection = 'X %s Y' % rtype
   930                 else:
   963                 else:
   931                     selection = '%s %s X' % (var, rtype)
   964                     selection = 'Y %s X' % rtype
   932                 rql = 'DELETE %s WHERE X eid %%(x)s' % selection
   965                 rql = 'DELETE %s WHERE X eid %%(x)s' % selection
   933                 # if user can delete the entity, he can delete all its relations
       
   934                 # without security checking
       
   935                 session.execute(rql, {'x': eid}, 'x', build_descr=False)
   966                 session.execute(rql, {'x': eid}, 'x', build_descr=False)
       
   967         self.system_source.delete_info(session, entity, sourceuri, extid)
   936 
   968 
   937     def locate_relation_source(self, session, subject, rtype, object):
   969     def locate_relation_source(self, session, subject, rtype, object):
   938         subjsource = self.source_from_eid(subject, session)
   970         subjsource = self.source_from_eid(subject, session)
   939         objsource = self.source_from_eid(object, session)
   971         objsource = self.source_from_eid(object, session)
   940         if not subjsource is objsource:
   972         if not subjsource is objsource:
   941             source = self.system_source
   973             source = self.system_source
   942             if not (subjsource.may_cross_relation(rtype) 
   974             if not (subjsource.may_cross_relation(rtype)
   943                     and objsource.may_cross_relation(rtype)):
   975                     and objsource.may_cross_relation(rtype)):
   944                 raise MultiSourcesError(
   976                 raise MultiSourcesError(
   945                     "relation %s can't be crossed among sources"
   977                     "relation %s can't be crossed among sources"
   946                     % rtype)
   978                     % rtype)
   947         elif not subjsource.support_relation(rtype):
   979         elif not subjsource.support_relation(rtype):
   981         relations = []
  1013         relations = []
   982         if source.should_call_hooks:
  1014         if source.should_call_hooks:
   983             self.hm.call_hooks('before_add_entity', session, entity=entity)
  1015             self.hm.call_hooks('before_add_entity', session, entity=entity)
   984         # XXX use entity.keys here since edited_attributes is not updated for
  1016         # XXX use entity.keys here since edited_attributes is not updated for
   985         # inline relations
  1017         # inline relations
   986         for attr in entity.keys():
  1018         for attr in entity.iterkeys():
   987             rschema = eschema.subjrels[attr]
  1019             rschema = eschema.subjrels[attr]
   988             if not rschema.final: # inlined relation
  1020             if not rschema.final: # inlined relation
   989                 relations.append((attr, entity[attr]))
  1021                 relations.append((attr, entity[attr]))
   990         entity.set_defaults()
  1022         entity.set_defaults()
   991         if session.is_hook_category_activated('integrity'):
  1023         if session.is_hook_category_activated('integrity'):
  1092                 self.hm.call_hooks('after_add_relation', session,
  1124                 self.hm.call_hooks('after_add_relation', session,
  1093                                     eidfrom=entity.eid, rtype=attr, eidto=value)
  1125                                     eidfrom=entity.eid, rtype=attr, eidto=value)
  1094 
  1126 
  1095     def glob_delete_entity(self, session, eid):
  1127     def glob_delete_entity(self, session, eid):
  1096         """delete an entity and all related entities from the repository"""
  1128         """delete an entity and all related entities from the repository"""
  1097         # call delete_info before hooks
  1129         entity = session.entity_from_eid(eid)
  1098         self._prepare_delete_info(session, eid)
  1130         etype, sourceuri, extid = self.type_and_source_from_eid(eid, session)
  1099         etype, uri, extid = self.type_and_source_from_eid(eid, session)
  1131         self._prepare_delete_info(session, entity, sourceuri)
  1100         if server.DEBUG & server.DBG_REPO:
  1132         if server.DEBUG & server.DBG_REPO:
  1101             print 'DELETE entity', etype, eid
  1133             print 'DELETE entity', etype, eid
  1102             if eid == 937:
  1134         source = self.sources_by_uri[sourceuri]
  1103                 server.DEBUG |= (server.DBG_SQL | server.DBG_RQL | server.DBG_MORE)
       
  1104         source = self.sources_by_uri[uri]
       
  1105         if source.should_call_hooks:
  1135         if source.should_call_hooks:
  1106             entity = session.entity_from_eid(eid)
       
  1107             self.hm.call_hooks('before_delete_entity', session, entity=entity)
  1136             self.hm.call_hooks('before_delete_entity', session, entity=entity)
  1108         self._delete_info(session, eid)
  1137         self._delete_info(session, entity, sourceuri, extid)
  1109         source.delete_entity(session, etype, eid)
  1138         source.delete_entity(session, entity)
  1110         if source.should_call_hooks:
  1139         if source.should_call_hooks:
  1111             self.hm.call_hooks('after_delete_entity', session, entity=entity)
  1140             self.hm.call_hooks('after_delete_entity', session, entity=entity)
  1112         # don't clear cache here this is done in a hook on commit
  1141         # don't clear cache here this is done in a hook on commit
  1113 
  1142 
  1114     def glob_add_relation(self, session, subject, rtype, object):
  1143     def glob_add_relation(self, session, subject, rtype, object):