server/repository.py
changeset 9821 2077c8da1893
parent 9800 029f9eafe574
child 9885 9f546848ba48
equal deleted inserted replaced
9819:95902c0b991b 9821:2077c8da1893
   650                                                   for attr in query_attrs),
   650                                                   for attr in query_attrs),
   651                                    query_attrs)
   651                                    query_attrs)
   652             return rset.rows
   652             return rset.rows
   653 
   653 
   654     def connect(self, login, **kwargs):
   654     def connect(self, login, **kwargs):
   655         """open a connection for a given user
   655         """open a session for a given user
   656 
   656 
   657         raise `AuthenticationError` if the authentication failed
   657         raise `AuthenticationError` if the authentication failed
   658         raise `ConnectionError` if we can't open a connection
   658         raise `ConnectionError` if we can't open a connection
   659         """
   659         """
   660         cnxprops = kwargs.pop('cnxprops', None)
   660         cnxprops = kwargs.pop('cnxprops', None)
   682 
   682 
   683     def execute(self, sessionid, rqlstring, args=None, build_descr=True,
   683     def execute(self, sessionid, rqlstring, args=None, build_descr=True,
   684                 txid=None):
   684                 txid=None):
   685         """execute a RQL query
   685         """execute a RQL query
   686 
   686 
   687         * rqlstring should be an unicode string or a plain ascii string
   687         * rqlstring should be a unicode string or a plain ascii string
   688         * args the optional parameters used in the query
   688         * args the optional parameters used in the query
   689         * build_descr is a flag indicating if the description should be
   689         * build_descr is a flag indicating if the description should be
   690           built on select queries
   690           built on select queries
   691         """
   691         """
   692         session = self._get_session(sessionid, setcnxset=True, txid=txid)
   692         session = self._get_session(sessionid, setcnxset=True, txid=txid)
   744         """raise `BadConnectionId` if the connection is no more valid, else
   744         """raise `BadConnectionId` if the connection is no more valid, else
   745         return its latest activity timestamp.
   745         return its latest activity timestamp.
   746         """
   746         """
   747         return self._get_session(sessionid, setcnxset=False).timestamp
   747         return self._get_session(sessionid, setcnxset=False).timestamp
   748 
   748 
       
   749     @deprecated('[3.19] use session or transaction data')
   749     def get_shared_data(self, sessionid, key, default=None, pop=False, txdata=False):
   750     def get_shared_data(self, sessionid, key, default=None, pop=False, txdata=False):
   750         """return value associated to key in the session's data dictionary or
   751         """return value associated to key in the session's data dictionary or
   751         session's transaction's data if `txdata` is true.
   752         session's transaction's data if `txdata` is true.
   752 
   753 
   753         If pop is True, value will be removed from the dictionary.
   754         If pop is True, value will be removed from the dictionary.
   756         `default` argument will be returned.
   757         `default` argument will be returned.
   757         """
   758         """
   758         session = self._get_session(sessionid, setcnxset=False)
   759         session = self._get_session(sessionid, setcnxset=False)
   759         return session.get_shared_data(key, default, pop, txdata)
   760         return session.get_shared_data(key, default, pop, txdata)
   760 
   761 
       
   762     @deprecated('[3.19] use session or transaction data')
   761     def set_shared_data(self, sessionid, key, value, txdata=False):
   763     def set_shared_data(self, sessionid, key, value, txdata=False):
   762         """set value associated to `key` in shared data
   764         """set value associated to `key` in shared data
   763 
   765 
   764         if `txdata` is true, the value will be added to the repository session's
   766         if `txdata` is true, the value will be added to the repository session's
   765         transaction's data which are cleared on commit/rollback of the current
   767         transaction's data which are cleared on commit/rollback of the current
   907         session.set_cnxset()
   909         session.set_cnxset()
   908         return session
   910         return session
   909 
   911 
   910     @contextmanager
   912     @contextmanager
   911     def internal_cnx(self):
   913     def internal_cnx(self):
   912         """return a Connection using internal user which have
   914         """Context manager returning a Connection using internal user which have
   913         every rights on the repository. The `safe` argument is dropped. all
   915         every access rights on the repository.
   914         hook are enabled by default.
   916 
   915 
   917         Beware that unlike the older :meth:`internal_session`, internal
   916         /!\ IN OPPOSITE OF THE OLDER INTERNAL_SESSION,
   918         connections have all hooks beside security enabled.
   917         /!\ INTERNAL CONNECTION HAVE ALL HOOKS ENABLED.
       
   918 
       
   919         This is to be used a context manager.
       
   920         """
   919         """
   921         with InternalSession(self) as session:
   920         with InternalSession(self) as session:
   922             with session.new_cnx() as cnx:
   921             with session.new_cnx() as cnx:
   923                 # equivalent to cnx.security_enabled(False, False) because
   922                 with cnx.security_enabled(read=False, write=False):
   924                 # InternalSession gives full read access
       
   925                 with cnx.allow_all_hooks_but('security'):
       
   926                     with cnx.ensure_cnx_set:
   923                     with cnx.ensure_cnx_set:
   927                         yield cnx
   924                         yield cnx
   928 
       
   929 
   925 
   930     def _get_session(self, sessionid, setcnxset=False, txid=None,
   926     def _get_session(self, sessionid, setcnxset=False, txid=None,
   931                      checkshuttingdown=True):
   927                      checkshuttingdown=True):
   932         """return the session associated with the given session identifier"""
   928         """return the session associated with the given session identifier"""
   933         if checkshuttingdown and self.shutting_down:
   929         if checkshuttingdown and self.shutting_down:
   943 
   939 
   944     # data sources handling ###################################################
   940     # data sources handling ###################################################
   945     # * correspondance between eid and (type, source)
   941     # * correspondance between eid and (type, source)
   946     # * correspondance between eid and local id (i.e. specific to a given source)
   942     # * correspondance between eid and local id (i.e. specific to a given source)
   947 
   943 
   948     def type_and_source_from_eid(self, eid, session):
   944     def type_and_source_from_eid(self, eid, cnx):
   949         """return a tuple `(type, extid, actual source uri)` for the entity of
   945         """return a tuple `(type, extid, actual source uri)` for the entity of
   950         the given `eid`
   946         the given `eid`
   951         """
   947         """
   952         try:
   948         try:
   953             eid = int(eid)
   949             eid = int(eid)
   954         except ValueError:
   950         except ValueError:
   955             raise UnknownEid(eid)
   951             raise UnknownEid(eid)
   956         try:
   952         try:
   957             return self._type_source_cache[eid]
   953             return self._type_source_cache[eid]
   958         except KeyError:
   954         except KeyError:
   959             etype, extid, auri = self.system_source.eid_type_source(session,
   955             etype, extid, auri = self.system_source.eid_type_source(cnx, eid)
   960                                                                     eid)
       
   961             self._type_source_cache[eid] = (etype, extid, auri)
   956             self._type_source_cache[eid] = (etype, extid, auri)
   962             return etype, extid, auri
   957             return etype, extid, auri
   963 
   958 
   964     def clear_caches(self, eids):
   959     def clear_caches(self, eids):
   965         etcache = self._type_source_cache
   960         etcache = self._type_source_cache
   973             except KeyError:
   968             except KeyError:
   974                 etype = None
   969                 etype = None
   975             rqlcache.pop( ('Any X WHERE X eid %s' % eid,), None)
   970             rqlcache.pop( ('Any X WHERE X eid %s' % eid,), None)
   976             self.system_source.clear_eid_cache(eid, etype)
   971             self.system_source.clear_eid_cache(eid, etype)
   977 
   972 
   978     def type_from_eid(self, eid, session):
   973     def type_from_eid(self, eid, cnx):
   979         """return the type of the entity with id <eid>"""
   974         """return the type of the entity with id <eid>"""
   980         return self.type_and_source_from_eid(eid, session)[0]
   975         return self.type_and_source_from_eid(eid, cnx)[0]
   981 
   976 
   982     def querier_cache_key(self, session, rql, args, eidkeys):
   977     def querier_cache_key(self, session, rql, args, eidkeys):
   983         cachekey = [rql]
   978         cachekey = [rql]
   984         for key in sorted(eidkeys):
   979         for key in sorted(eidkeys):
   985             try:
   980             try:
  1025         try:
  1020         try:
  1026             # bw compat: cnx may be a session, get at the Connection
  1021             # bw compat: cnx may be a session, get at the Connection
  1027             cnx = cnx._cnx
  1022             cnx = cnx._cnx
  1028         except AttributeError:
  1023         except AttributeError:
  1029             pass
  1024             pass
  1030         eid = self.system_source.extid2eid(cnx, extid)
  1025         with cnx.ensure_cnx_set:
       
  1026             eid = self.system_source.extid2eid(cnx, extid)
  1031         if eid is not None:
  1027         if eid is not None:
  1032             self._extid_cache[extid] = eid
  1028             self._extid_cache[extid] = eid
  1033             self._type_source_cache[eid] = (etype, extid, source.uri)
  1029             self._type_source_cache[eid] = (etype, extid, source.uri)
  1034             return eid
  1030             return eid
  1035         if not insert:
  1031         if not insert:
  1036             return
  1032             return
  1037         # no link between extid and eid, create one
  1033         # no link between extid and eid, create one
  1038         try:
  1034         with cnx.ensure_cnx_set:
  1039             eid = self.system_source.create_eid(cnx)
  1035             # write query, ensure connection's mode is 'write' so connections
  1040             self._extid_cache[extid] = eid
  1036             # won't be released until commit/rollback
  1041             self._type_source_cache[eid] = (etype, extid, source.uri)
  1037             cnx.mode = 'write'
  1042             entity = source.before_entity_insertion(
  1038             try:
  1043                 cnx, extid, etype, eid, sourceparams)
  1039                 eid = self.system_source.create_eid(cnx)
  1044             if source.should_call_hooks:
  1040                 self._extid_cache[extid] = eid
  1045                 # get back a copy of operation for later restore if necessary,
  1041                 self._type_source_cache[eid] = (etype, extid, source.uri)
  1046                 # see below
  1042                 entity = source.before_entity_insertion(
  1047                 pending_operations = cnx.pending_operations[:]
  1043                     cnx, extid, etype, eid, sourceparams)
  1048                 self.hm.call_hooks('before_add_entity', cnx, entity=entity)
       
  1049             self.add_info(cnx, entity, source, extid)
       
  1050             source.after_entity_insertion(cnx, extid, entity, sourceparams)
       
  1051             if source.should_call_hooks:
       
  1052                 self.hm.call_hooks('after_add_entity', cnx, entity=entity)
       
  1053             return eid
       
  1054         except Exception:
       
  1055             # XXX do some cleanup manually so that the transaction has a
       
  1056             # chance to be commited, with simply this entity discarded
       
  1057             self._extid_cache.pop(extid, None)
       
  1058             self._type_source_cache.pop(eid, None)
       
  1059             if 'entity' in locals():
       
  1060                 hook.CleanupDeletedEidsCacheOp.get_instance(cnx).add_data(entity.eid)
       
  1061                 self.system_source.delete_info_multi(cnx, [entity])
       
  1062                 if source.should_call_hooks:
  1044                 if source.should_call_hooks:
  1063                     cnx.pending_operations = pending_operations
  1045                     # get back a copy of operation for later restore if
  1064             raise
  1046                     # necessary, see below
       
  1047                     pending_operations = cnx.pending_operations[:]
       
  1048                     self.hm.call_hooks('before_add_entity', cnx, entity=entity)
       
  1049                 self.add_info(cnx, entity, source, extid)
       
  1050                 source.after_entity_insertion(cnx, extid, entity, sourceparams)
       
  1051                 if source.should_call_hooks:
       
  1052                     self.hm.call_hooks('after_add_entity', cnx, entity=entity)
       
  1053                 return eid
       
  1054             except Exception:
       
  1055                 # XXX do some cleanup manually so that the transaction has a
       
  1056                 # chance to be commited, with simply this entity discarded
       
  1057                 self._extid_cache.pop(extid, None)
       
  1058                 self._type_source_cache.pop(eid, None)
       
  1059                 if 'entity' in locals():
       
  1060                     hook.CleanupDeletedEidsCacheOp.get_instance(cnx).add_data(entity.eid)
       
  1061                     self.system_source.delete_info_multi(cnx, [entity])
       
  1062                     if source.should_call_hooks:
       
  1063                         cnx.pending_operations = pending_operations
       
  1064                 raise
  1065 
  1065 
  1066     def add_info(self, session, entity, source, extid=None):
  1066     def add_info(self, session, entity, source, extid=None):
  1067         """add type and source info for an eid into the system table,
  1067         """add type and source info for an eid into the system table,
  1068         and index the entity with the full text index
  1068         and index the entity with the full text index
  1069         """
  1069         """
  1261                     hm.call_hooks('after_delete_relation', cnx,
  1261                     hm.call_hooks('after_delete_relation', cnx,
  1262                                   eidfrom=entity.eid, rtype=attr, eidto=prevvalue)
  1262                                   eidfrom=entity.eid, rtype=attr, eidto=prevvalue)
  1263                     if relcache is not None:
  1263                     if relcache is not None:
  1264                         cnx.update_rel_cache_del(entity.eid, attr, prevvalue)
  1264                         cnx.update_rel_cache_del(entity.eid, attr, prevvalue)
  1265                 del_existing_rel_if_needed(cnx, entity.eid, attr, value)
  1265                 del_existing_rel_if_needed(cnx, entity.eid, attr, value)
  1266                 if relcache is not None:
  1266                 cnx.update_rel_cache_add(entity.eid, attr, value)
  1267                     cnx.update_rel_cache_add(entity.eid, attr, value)
       
  1268                 else:
       
  1269                     entity.cw_set_relation_cache(attr, 'subject',
       
  1270                                                  cnx.eid_rset(value))
       
  1271                 hm.call_hooks('after_add_relation', cnx,
  1267                 hm.call_hooks('after_add_relation', cnx,
  1272                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1268                               eidfrom=entity.eid, rtype=attr, eidto=value)
  1273         finally:
  1269         finally:
  1274             if orig_edited is not None:
  1270             if orig_edited is not None:
  1275                 entity.cw_edited = orig_edited
  1271                 entity.cw_edited = orig_edited