server/session.py
changeset 9112 c859c7cef346
parent 9110 ed8b383d94fd
child 9113 af6efc15fc90
equal deleted inserted replaced
9111:9c867d852067 9112:c859c7cef346
    29 
    29 
    30 from logilab.common.deprecation import deprecated
    30 from logilab.common.deprecation import deprecated
    31 from logilab.common.textutils import unormalize
    31 from logilab.common.textutils import unormalize
    32 from logilab.common.registry import objectify_predicate
    32 from logilab.common.registry import objectify_predicate
    33 
    33 
    34 from cubicweb import UnknownEid, QueryError, schema, server
    34 from cubicweb import UnknownEid, QueryError, schema, server, ProgrammingError
    35 from cubicweb.req import RequestSessionBase
    35 from cubicweb.req import RequestSessionBase
    36 from cubicweb.utils import make_uid
    36 from cubicweb.utils import make_uid
    37 from cubicweb.rqlrewrite import RQLRewriter
    37 from cubicweb.rqlrewrite import RQLRewriter
    38 from cubicweb.server import ShuttingDown
    38 from cubicweb.server import ShuttingDown
    39 from cubicweb.server.edition import EditedEntity
    39 from cubicweb.server.edition import EditedEntity
   369     def wrapper(cnx, *args, **kwargs):
   369     def wrapper(cnx, *args, **kwargs):
   370         with cnx.ensure_cnx_set:
   370         with cnx.ensure_cnx_set:
   371             return func(cnx, *args, **kwargs)
   371             return func(cnx, *args, **kwargs)
   372     return wrapper
   372     return wrapper
   373 
   373 
       
   374 def _open_only(func):
       
   375     """decorator for Connection method that check it is open"""
       
   376     @functools.wraps(func)
       
   377     def check_open(cnx, *args, **kwargs):
       
   378         if not cnx._open:
       
   379             raise ProgrammingError('Closed Connection: %s'
       
   380                                     % cnx.connectionid)
       
   381         return func(cnx, *args, **kwargs)
       
   382     return check_open
   374 
   383 
   375 class Connection(RequestSessionBase):
   384 class Connection(RequestSessionBase):
   376     """Repository Connection
   385     """Repository Connection
   377 
   386 
   378     Holds all connection related data
   387     Holds all connection related data
   533 
   542 
   534     @property
   543     @property
   535     def rql_rewriter(self):
   544     def rql_rewriter(self):
   536         return self._rewriter
   545         return self._rewriter
   537 
   546 
       
   547     @_open_only
   538     def get_shared_data(self, key, default=None, pop=False, txdata=False):
   548     def get_shared_data(self, key, default=None, pop=False, txdata=False):
   539         """return value associated to `key` in session data"""
   549         """return value associated to `key` in session data"""
   540         if txdata:
   550         if txdata:
   541             data = self.transaction_data
   551             data = self.transaction_data
   542         else:
   552         else:
   544         if pop:
   554         if pop:
   545             return data.pop(key, default)
   555             return data.pop(key, default)
   546         else:
   556         else:
   547             return data.get(key, default)
   557             return data.get(key, default)
   548 
   558 
       
   559     @_open_only
   549     def set_shared_data(self, key, value, txdata=False):
   560     def set_shared_data(self, key, value, txdata=False):
   550         """set value associated to `key` in session data"""
   561         """set value associated to `key` in session data"""
   551         if txdata:
   562         if txdata:
   552             self.transaction_data[key] = value
   563             self.transaction_data[key] = value
   553         else:
   564         else:
   564         self.local_perm_cache.clear()
   575         self.local_perm_cache.clear()
   565         self.rewriter = RQLRewriter(self)
   576         self.rewriter = RQLRewriter(self)
   566 
   577 
   567     # Connection Set Management ###############################################
   578     # Connection Set Management ###############################################
   568     @property
   579     @property
       
   580     @_open_only
   569     def cnxset(self):
   581     def cnxset(self):
   570         return self._cnxset
   582         return self._cnxset
   571 
   583 
   572     @cnxset.setter
   584     @cnxset.setter
       
   585     @_open_only
   573     def cnxset(self, new_cnxset):
   586     def cnxset(self, new_cnxset):
   574         with self._cnxset_tracker:
   587         with self._cnxset_tracker:
   575             old_cnxset = self._cnxset
   588             old_cnxset = self._cnxset
   576             if new_cnxset is old_cnxset:
   589             if new_cnxset is old_cnxset:
   577                 return #nothing to do
   590                 return #nothing to do
   582             if new_cnxset is not None:
   595             if new_cnxset is not None:
   583                 self._cnxset_tracker.record(self.connectionid, new_cnxset)
   596                 self._cnxset_tracker.record(self.connectionid, new_cnxset)
   584                 self._cnxset = new_cnxset
   597                 self._cnxset = new_cnxset
   585                 self.ctx_count += 1
   598                 self.ctx_count += 1
   586 
   599 
       
   600     @_open_only
   587     def _set_cnxset(self):
   601     def _set_cnxset(self):
   588         """the connection need a connections set to execute some queries"""
   602         """the connection need a connections set to execute some queries"""
   589         if self.cnxset is None:
   603         if self.cnxset is None:
   590             cnxset = self.repo._get_cnxset()
   604             cnxset = self.repo._get_cnxset()
   591             try:
   605             try:
   598             except:
   612             except:
   599                 self.repo._free_cnxset(cnxset)
   613                 self.repo._free_cnxset(cnxset)
   600                 raise
   614                 raise
   601         return self.cnxset
   615         return self.cnxset
   602 
   616 
       
   617     @_open_only
   603     def _free_cnxset(self, ignoremode=False):
   618     def _free_cnxset(self, ignoremode=False):
   604         """the connection is no longer using its connections set, at least for some time"""
   619         """the connection is no longer using its connections set, at least for some time"""
   605         # cnxset may be none if no operation has been done since last commit
   620         # cnxset may be none if no operation has been done since last commit
   606         # or rollback
   621         # or rollback
   607         cnxset = self.cnxset
   622         cnxset = self.cnxset
   621         return self._free_cnxset(ignoremode=ignoremode)
   636         return self._free_cnxset(ignoremode=ignoremode)
   622 
   637 
   623 
   638 
   624     @property
   639     @property
   625     @contextmanager
   640     @contextmanager
       
   641     @_open_only
   626     def ensure_cnx_set(self):
   642     def ensure_cnx_set(self):
   627         assert self._cnxset_count >= 0
   643         assert self._cnxset_count >= 0
   628         if self._cnxset_count == 0:
   644         if self._cnxset_count == 0:
   629             self._set_cnxset()
   645             self._set_cnxset()
   630         try:
   646         try:
   646     # an acceptable risk. Anyway we could activate it or not according to a
   662     # an acceptable risk. Anyway we could activate it or not according to a
   647     # configuration option
   663     # configuration option
   648 
   664 
   649     def set_entity_cache(self, entity):
   665     def set_entity_cache(self, entity):
   650         """Add `entity` to the connection entity cache"""
   666         """Add `entity` to the connection entity cache"""
       
   667         #XXX not using _open_only because before at creation time. _set_user
       
   668         # call this function to cache the Connection user.
       
   669         if entity.cw_etype != 'CWUser' and not self._open:
       
   670             raise ProgrammingError('Closed Connection: %s'
       
   671                                     % self.connectionid)
   651         ecache = self.transaction_data.setdefault('ecache', {})
   672         ecache = self.transaction_data.setdefault('ecache', {})
   652         ecache.setdefault(entity.eid, entity)
   673         ecache.setdefault(entity.eid, entity)
   653 
   674 
       
   675     @_open_only
   654     def entity_cache(self, eid):
   676     def entity_cache(self, eid):
   655         """get cache entity for `eid`"""
   677         """get cache entity for `eid`"""
   656         return self.transaction_data['ecache'][eid]
   678         return self.transaction_data['ecache'][eid]
   657 
   679 
       
   680     @_open_only
   658     def cached_entities(self):
   681     def cached_entities(self):
   659         """return the whole entity cache"""
   682         """return the whole entity cache"""
   660         return self.transaction_data.get('ecache', {}).values()
   683         return self.transaction_data.get('ecache', {}).values()
   661 
   684 
       
   685     @_open_only
   662     def drop_entity_cache(self, eid=None):
   686     def drop_entity_cache(self, eid=None):
   663         """drop entity from the cache
   687         """drop entity from the cache
   664 
   688 
   665         If eid is None, the whole cache is dropped"""
   689         If eid is None, the whole cache is dropped"""
   666         if eid is None:
   690         if eid is None:
   668         else:
   692         else:
   669             del self.transaction_data['ecache'][eid]
   693             del self.transaction_data['ecache'][eid]
   670 
   694 
   671     # relations handling #######################################################
   695     # relations handling #######################################################
   672 
   696 
       
   697     @_open_only
   673     def add_relation(self, fromeid, rtype, toeid):
   698     def add_relation(self, fromeid, rtype, toeid):
   674         """provide direct access to the repository method to add a relation.
   699         """provide direct access to the repository method to add a relation.
   675 
   700 
   676         This is equivalent to the following rql query:
   701         This is equivalent to the following rql query:
   677 
   702 
   681         You may use this in hooks when you know both eids of the relation you
   706         You may use this in hooks when you know both eids of the relation you
   682         want to add.
   707         want to add.
   683         """
   708         """
   684         self.add_relations([(rtype, [(fromeid,  toeid)])])
   709         self.add_relations([(rtype, [(fromeid,  toeid)])])
   685 
   710 
       
   711     @_open_only
   686     def add_relations(self, relations):
   712     def add_relations(self, relations):
   687         '''set many relation using a shortcut similar to the one in add_relation
   713         '''set many relation using a shortcut similar to the one in add_relation
   688 
   714 
   689         relations is a list of 2-uples, the first element of each
   715         relations is a list of 2-uples, the first element of each
   690         2-uple is the rtype, and the second is a list of (fromeid,
   716         2-uple is the rtype, and the second is a list of (fromeid,
   708             self.repo.glob_add_relations(self, relations_dict)
   734             self.repo.glob_add_relations(self, relations_dict)
   709             for edited in edited_entities.itervalues():
   735             for edited in edited_entities.itervalues():
   710                 self.repo.glob_update_entity(self, edited)
   736                 self.repo.glob_update_entity(self, edited)
   711 
   737 
   712 
   738 
       
   739     @_open_only
   713     def delete_relation(self, fromeid, rtype, toeid):
   740     def delete_relation(self, fromeid, rtype, toeid):
   714         """provide direct access to the repository method to delete a relation.
   741         """provide direct access to the repository method to delete a relation.
   715 
   742 
   716         This is equivalent to the following rql query:
   743         This is equivalent to the following rql query:
   717 
   744 
   729             else:
   756             else:
   730                 self.repo.glob_delete_relation(self, fromeid, rtype, toeid)
   757                 self.repo.glob_delete_relation(self, fromeid, rtype, toeid)
   731 
   758 
   732     # relations cache handling #################################################
   759     # relations cache handling #################################################
   733 
   760 
       
   761     @_open_only
   734     def update_rel_cache_add(self, subject, rtype, object, symmetric=False):
   762     def update_rel_cache_add(self, subject, rtype, object, symmetric=False):
   735         self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
   763         self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
   736         if symmetric:
   764         if symmetric:
   737             self._update_entity_rel_cache_add(object, rtype, 'subject', subject)
   765             self._update_entity_rel_cache_add(object, rtype, 'subject', subject)
   738         else:
   766         else:
   739             self._update_entity_rel_cache_add(object, rtype, 'object', subject)
   767             self._update_entity_rel_cache_add(object, rtype, 'object', subject)
   740 
   768 
       
   769     @_open_only
   741     def update_rel_cache_del(self, subject, rtype, object, symmetric=False):
   770     def update_rel_cache_del(self, subject, rtype, object, symmetric=False):
   742         self._update_entity_rel_cache_del(subject, rtype, 'subject', object)
   771         self._update_entity_rel_cache_del(subject, rtype, 'subject', object)
   743         if symmetric:
   772         if symmetric:
   744             self._update_entity_rel_cache_del(object, rtype, 'object', object)
   773             self._update_entity_rel_cache_del(object, rtype, 'object', object)
   745         else:
   774         else:
   746             self._update_entity_rel_cache_del(object, rtype, 'object', subject)
   775             self._update_entity_rel_cache_del(object, rtype, 'object', subject)
   747 
   776 
       
   777     @_open_only
   748     def _update_entity_rel_cache_add(self, eid, rtype, role, targeteid):
   778     def _update_entity_rel_cache_add(self, eid, rtype, role, targeteid):
   749         try:
   779         try:
   750             entity = self.entity_cache(eid)
   780             entity = self.entity_cache(eid)
   751         except KeyError:
   781         except KeyError:
   752             return
   782             return
   767             rset.rowcount += 1
   797             rset.rowcount += 1
   768             entities.append(targetentity)
   798             entities.append(targetentity)
   769             entity._cw_related_cache['%s_%s' % (rtype, role)] = (
   799             entity._cw_related_cache['%s_%s' % (rtype, role)] = (
   770                 rset, tuple(entities))
   800                 rset, tuple(entities))
   771 
   801 
       
   802     @_open_only
   772     def _update_entity_rel_cache_del(self, eid, rtype, role, targeteid):
   803     def _update_entity_rel_cache_del(self, eid, rtype, role, targeteid):
   773         try:
   804         try:
   774             entity = self.entity_cache(eid)
   805             entity = self.entity_cache(eid)
   775         except KeyError:
   806         except KeyError:
   776             return
   807             return
   798 
   829 
   799     # Tracking of entity added of removed in the transaction ##################
   830     # Tracking of entity added of removed in the transaction ##################
   800     #
   831     #
   801     # Those are function to  allows cheap call from client in other process.
   832     # Those are function to  allows cheap call from client in other process.
   802 
   833 
       
   834     @_open_only
   803     def deleted_in_transaction(self, eid):
   835     def deleted_in_transaction(self, eid):
   804         """return True if the entity of the given eid is being deleted in the
   836         """return True if the entity of the given eid is being deleted in the
   805         current transaction
   837         current transaction
   806         """
   838         """
   807         return eid in self.transaction_data.get('pendingeids', ())
   839         return eid in self.transaction_data.get('pendingeids', ())
   808 
   840 
       
   841     @_open_only
   809     def added_in_transaction(self, eid):
   842     def added_in_transaction(self, eid):
   810         """return True if the entity of the given eid is being created in the
   843         """return True if the entity of the given eid is being created in the
   811         current transaction
   844         current transaction
   812         """
   845         """
   813         return eid in self.transaction_data.get('neweids', ())
   846         return eid in self.transaction_data.get('neweids', ())
   814 
   847 
   815     # Operation management ####################################################
   848     # Operation management ####################################################
   816 
   849 
       
   850     @_open_only
   817     def add_operation(self, operation, index=None):
   851     def add_operation(self, operation, index=None):
   818         """add an operation to be executed at the end of the transaction"""
   852         """add an operation to be executed at the end of the transaction"""
   819         if index is None:
   853         if index is None:
   820             self.pending_operations.append(operation)
   854             self.pending_operations.append(operation)
   821         else:
   855         else:
   822             self.pending_operations.insert(index, operation)
   856             self.pending_operations.insert(index, operation)
   823 
   857 
   824     # Hooks control ###########################################################
   858     # Hooks control ###########################################################
   825 
   859 
       
   860     @_open_only
   826     def allow_all_hooks_but(self, *categories):
   861     def allow_all_hooks_but(self, *categories):
   827         return _hooks_control(self, HOOKS_ALLOW_ALL, *categories)
   862         return _hooks_control(self, HOOKS_ALLOW_ALL, *categories)
   828 
   863 
       
   864     @_open_only
   829     def deny_all_hooks_but(self, *categories):
   865     def deny_all_hooks_but(self, *categories):
   830         return _hooks_control(self, HOOKS_DENY_ALL, *categories)
   866         return _hooks_control(self, HOOKS_DENY_ALL, *categories)
   831 
   867 
       
   868     @_open_only
   832     def disable_hook_categories(self, *categories):
   869     def disable_hook_categories(self, *categories):
   833         """disable the given hook categories:
   870         """disable the given hook categories:
   834 
   871 
   835         - on HOOKS_DENY_ALL mode, ensure those categories are not enabled
   872         - on HOOKS_DENY_ALL mode, ensure those categories are not enabled
   836         - on HOOKS_ALLOW_ALL mode, ensure those categories are disabled
   873         - on HOOKS_ALLOW_ALL mode, ensure those categories are disabled
   846             disabledcats = self.disabled_hook_cats
   883             disabledcats = self.disabled_hook_cats
   847             changes = categories - disabledcats
   884             changes = categories - disabledcats
   848             disabledcats |= changes # changes is small hence faster
   885             disabledcats |= changes # changes is small hence faster
   849         return tuple(changes)
   886         return tuple(changes)
   850 
   887 
       
   888     @_open_only
   851     def enable_hook_categories(self, *categories):
   889     def enable_hook_categories(self, *categories):
   852         """enable the given hook categories:
   890         """enable the given hook categories:
   853 
   891 
   854         - on HOOKS_DENY_ALL mode, ensure those categories are enabled
   892         - on HOOKS_DENY_ALL mode, ensure those categories are enabled
   855         - on HOOKS_ALLOW_ALL mode, ensure those categories are not disabled
   893         - on HOOKS_ALLOW_ALL mode, ensure those categories are not disabled
   865             disabledcats = self.disabled_hook_cats
   903             disabledcats = self.disabled_hook_cats
   866             changes = disabledcats & categories
   904             changes = disabledcats & categories
   867             disabledcats -= changes # changes is small hence faster
   905             disabledcats -= changes # changes is small hence faster
   868         return tuple(changes)
   906         return tuple(changes)
   869 
   907 
       
   908     @_open_only
   870     def is_hook_category_activated(self, category):
   909     def is_hook_category_activated(self, category):
   871         """return a boolean telling if the given category is currently activated
   910         """return a boolean telling if the given category is currently activated
   872         or not
   911         or not
   873         """
   912         """
   874         if self.hooks_mode is HOOKS_DENY_ALL:
   913         if self.hooks_mode is HOOKS_DENY_ALL:
   875             return category in self.enabled_hook_cats
   914             return category in self.enabled_hook_cats
   876         return category not in self.disabled_hook_cats
   915         return category not in self.disabled_hook_cats
   877 
   916 
       
   917     @_open_only
   878     def is_hook_activated(self, hook):
   918     def is_hook_activated(self, hook):
   879         """return a boolean telling if the given hook class is currently
   919         """return a boolean telling if the given hook class is currently
   880         activated or not
   920         activated or not
   881         """
   921         """
   882         return self.is_hook_category_activated(hook.category)
   922         return self.is_hook_category_activated(hook.category)
   883 
   923 
   884     # Security management #####################################################
   924     # Security management #####################################################
   885 
   925 
       
   926     @_open_only
   886     def security_enabled(self, read=None, write=None):
   927     def security_enabled(self, read=None, write=None):
   887         return _security_enabled(self, read=read, write=write)
   928         return _security_enabled(self, read=read, write=write)
   888 
   929 
   889     @property
   930     @property
       
   931     @_open_only
   890     def read_security(self):
   932     def read_security(self):
   891         return self._read_security
   933         return self._read_security
   892 
   934 
   893     @read_security.setter
   935     @read_security.setter
       
   936     @_open_only
   894     def read_security(self, activated):
   937     def read_security(self, activated):
   895         oldmode = self._read_security
   938         oldmode = self._read_security
   896         self._read_security = activated
   939         self._read_security = activated
   897         # running_dbapi_query used to detect hooks triggered by a 'dbapi' query
   940         # running_dbapi_query used to detect hooks triggered by a 'dbapi' query
   898         # (eg not issued on the session). This is tricky since we the execution
   941         # (eg not issued on the session). This is tricky since we the execution
   914         self.running_dbapi_query = (oldmode is DEFAULT_SECURITY
   957         self.running_dbapi_query = (oldmode is DEFAULT_SECURITY
   915                                     or activated is DEFAULT_SECURITY)
   958                                     or activated is DEFAULT_SECURITY)
   916 
   959 
   917     # undo support ############################################################
   960     # undo support ############################################################
   918 
   961 
       
   962     @_open_only
   919     def ertype_supports_undo(self, ertype):
   963     def ertype_supports_undo(self, ertype):
   920         return self.undo_actions and ertype not in NO_UNDO_TYPES
   964         return self.undo_actions and ertype not in NO_UNDO_TYPES
   921 
   965 
       
   966     @_open_only
   922     def transaction_uuid(self, set=True):
   967     def transaction_uuid(self, set=True):
   923         uuid = self.transaction_data.get('tx_uuid')
   968         uuid = self.transaction_data.get('tx_uuid')
   924         if set and uuid is None:
   969         if set and uuid is None:
   925             self.transaction_data['tx_uuid'] = uuid = uuid4().hex
   970             self.transaction_data['tx_uuid'] = uuid = uuid4().hex
   926             self.repo.system_source.start_undoable_transaction(self, uuid)
   971             self.repo.system_source.start_undoable_transaction(self, uuid)
   927         return uuid
   972         return uuid
   928 
   973 
       
   974     @_open_only
   929     def transaction_inc_action_counter(self):
   975     def transaction_inc_action_counter(self):
   930         num = self.transaction_data.setdefault('tx_action_count', 0) + 1
   976         num = self.transaction_data.setdefault('tx_action_count', 0) + 1
   931         self.transaction_data['tx_action_count'] = num
   977         self.transaction_data['tx_action_count'] = num
   932         return num
   978         return num
   933     # db-api like interface ###################################################
   979     # db-api like interface ###################################################
   934 
   980 
       
   981     @_open_only
   935     def source_defs(self):
   982     def source_defs(self):
   936         return self.repo.source_defs()
   983         return self.repo.source_defs()
   937 
   984 
   938     @_with_cnx_set
   985     @_with_cnx_set
       
   986     @_open_only
   939     def describe(self, eid, asdict=False):
   987     def describe(self, eid, asdict=False):
   940         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
   988         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
   941         metas = self.repo.type_and_source_from_eid(eid, self)
   989         metas = self.repo.type_and_source_from_eid(eid, self)
   942         if asdict:
   990         if asdict:
   943             return dict(zip(('type', 'source', 'extid', 'asource'), metas))
   991             return dict(zip(('type', 'source', 'extid', 'asource'), metas))
   944        # XXX :-1 for cw compat, use asdict=True for full information
   992        # XXX :-1 for cw compat, use asdict=True for full information
   945         return metas[:-1]
   993         return metas[:-1]
   946 
   994 
   947     @_with_cnx_set
   995     @_with_cnx_set
       
   996     @_open_only
   948     def source_from_eid(self, eid):
   997     def source_from_eid(self, eid):
   949         """return the source where the entity with id <eid> is located"""
   998         """return the source where the entity with id <eid> is located"""
   950         return self.repo.source_from_eid(eid, self)
   999         return self.repo.source_from_eid(eid, self)
   951 
  1000 
   952     # core method #############################################################
  1001     # core method #############################################################
   953 
  1002 
   954     @_with_cnx_set
  1003     @_with_cnx_set
       
  1004     @_open_only
   955     def execute(self, rql, kwargs=None, eid_key=None, build_descr=True):
  1005     def execute(self, rql, kwargs=None, eid_key=None, build_descr=True):
   956         """db-api like method directly linked to the querier execute method.
  1006         """db-api like method directly linked to the querier execute method.
   957 
  1007 
   958         See :meth:`cubicweb.dbapi.Cursor.execute` documentation.
  1008         See :meth:`cubicweb.dbapi.Cursor.execute` documentation.
   959         """
  1009         """
   964         rset = self._execute(self, rql, kwargs, build_descr)
  1014         rset = self._execute(self, rql, kwargs, build_descr)
   965         rset.req = self
  1015         rset.req = self
   966         self._session_timestamp.touch()
  1016         self._session_timestamp.touch()
   967         return rset
  1017         return rset
   968 
  1018 
       
  1019     @_open_only
   969     def rollback(self, free_cnxset=True, reset_pool=None):
  1020     def rollback(self, free_cnxset=True, reset_pool=None):
   970         """rollback the current transaction"""
  1021         """rollback the current transaction"""
   971         if reset_pool is not None:
  1022         if reset_pool is not None:
   972             warn('[3.13] use free_cnxset argument instead for reset_pool',
  1023             warn('[3.13] use free_cnxset argument instead for reset_pool',
   973                  DeprecationWarning, stacklevel=2)
  1024                  DeprecationWarning, stacklevel=2)
   994             self._session_timestamp.touch()
  1045             self._session_timestamp.touch()
   995             if free_cnxset:
  1046             if free_cnxset:
   996                 self.free_cnxset(ignoremode=True)
  1047                 self.free_cnxset(ignoremode=True)
   997             self.clear()
  1048             self.clear()
   998 
  1049 
       
  1050     @_open_only
   999     def commit(self, free_cnxset=True, reset_pool=None):
  1051     def commit(self, free_cnxset=True, reset_pool=None):
  1000         """commit the current session's transaction"""
  1052         """commit the current session's transaction"""
  1001         if reset_pool is not None:
  1053         if reset_pool is not None:
  1002             warn('[3.13] use free_cnxset argument instead for reset_pool',
  1054             warn('[3.13] use free_cnxset argument instead for reset_pool',
  1003                  DeprecationWarning, stacklevel=2)
  1055                  DeprecationWarning, stacklevel=2)
  1085             self.clear()
  1137             self.clear()
  1086 
  1138 
  1087     # resource accessors ######################################################
  1139     # resource accessors ######################################################
  1088 
  1140 
  1089     @_with_cnx_set
  1141     @_with_cnx_set
       
  1142     @_open_only
  1090     def call_service(self, regid, **kwargs):
  1143     def call_service(self, regid, **kwargs):
  1091         json.dumps(kwargs) # This line ensure that people use serialisable
  1144         json.dumps(kwargs) # This line ensure that people use serialisable
  1092                            # argument for call service. this is very important
  1145                            # argument for call service. this is very important
  1093                            # to enforce that from start to make sure RPC
  1146                            # to enforce that from start to make sure RPC
  1094                            # version is available.
  1147                            # version is available.
  1100                            # from start to make sure RPC version is
  1153                            # from start to make sure RPC version is
  1101                            # available.
  1154                            # available.
  1102         return result
  1155         return result
  1103 
  1156 
  1104     @_with_cnx_set
  1157     @_with_cnx_set
       
  1158     @_open_only
  1105     def system_sql(self, sql, args=None, rollback_on_failure=True):
  1159     def system_sql(self, sql, args=None, rollback_on_failure=True):
  1106         """return a sql cursor on the system database"""
  1160         """return a sql cursor on the system database"""
  1107         if sql.split(None, 1)[0].upper() != 'SELECT':
  1161         if sql.split(None, 1)[0].upper() != 'SELECT':
  1108             self.mode = 'write'
  1162             self.mode = 'write'
  1109         source = self.cnxset.source('system')
  1163         source = self.cnxset.source('system')
  1114                 raise
  1168                 raise
  1115             source.warning("trying to reconnect")
  1169             source.warning("trying to reconnect")
  1116             self.cnxset.reconnect(source)
  1170             self.cnxset.reconnect(source)
  1117             return source.doexec(self, sql, args, rollback=rollback_on_failure)
  1171             return source.doexec(self, sql, args, rollback=rollback_on_failure)
  1118 
  1172 
       
  1173     @_open_only
  1119     def rtype_eids_rdef(self, rtype, eidfrom, eidto):
  1174     def rtype_eids_rdef(self, rtype, eidfrom, eidto):
  1120         # use type_and_source_from_eid instead of type_from_eid for optimization
  1175         # use type_and_source_from_eid instead of type_from_eid for optimization
  1121         # (avoid two extra methods call)
  1176         # (avoid two extra methods call)
  1122         subjtype = self.repo.type_and_source_from_eid(eidfrom, self)[0]
  1177         subjtype = self.repo.type_and_source_from_eid(eidfrom, self)[0]
  1123         objtype = self.repo.type_and_source_from_eid(eidto, self)[0]
  1178         objtype = self.repo.type_and_source_from_eid(eidto, self)[0]