server/migractions.py
brancholdstable
changeset 4985 02b52bf9f5f8
parent 4950 bca0873c0d6e
child 4951 7dc54e12c606
equal deleted inserted replaced
4563:c25da7573ebd 4985:02b52bf9f5f8
    27 from glob import glob
    27 from glob import glob
    28 from warnings import warn
    28 from warnings import warn
    29 
    29 
    30 from logilab.common.deprecation import deprecated
    30 from logilab.common.deprecation import deprecated
    31 from logilab.common.decorators import cached, clear_cache
    31 from logilab.common.decorators import cached, clear_cache
    32 from logilab.common.adbh import get_adv_func_helper
       
    33 
    32 
    34 from yams.constraints import SizeConstraint
    33 from yams.constraints import SizeConstraint
    35 from yams.schema2sql import eschema2sql, rschema2sql
    34 from yams.schema2sql import eschema2sql, rschema2sql
    36 
    35 
    37 from cubicweb import AuthenticationError, ETYPE_NAME_MAP
    36 from cubicweb import AuthenticationError
    38 from cubicweb.schema import (META_RTYPES, VIRTUAL_RTYPES,
    37 from cubicweb.schema import (META_RTYPES, VIRTUAL_RTYPES,
    39                              CubicWebRelationSchema, order_eschemas)
    38                              CubicWebRelationSchema, order_eschemas)
    40 from cubicweb.dbapi import get_repository, repo_connect
    39 from cubicweb.dbapi import get_repository, repo_connect
    41 from cubicweb.common.migration import MigrationHelper, yes
    40 from cubicweb.migration import MigrationHelper, yes
    42 
    41 
    43 try:
    42 try:
    44     from cubicweb.server import SOURCE_TYPES, schemaserial as ss
    43     from cubicweb.server import SOURCE_TYPES, schemaserial as ss
    45     from cubicweb.server.utils import manager_userpasswd, ask_source_config
    44     from cubicweb.server.utils import manager_userpasswd, ask_source_config
    46     from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
    45     from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
   144         # backup
   143         # backup
   145         tmpdir = tempfile.mkdtemp(dir=instbkdir)
   144         tmpdir = tempfile.mkdtemp(dir=instbkdir)
   146         try:
   145         try:
   147             for source in repo.sources:
   146             for source in repo.sources:
   148                 try:
   147                 try:
   149                     source.backup(osp.join(tmpdir, source.uri))
   148                     source.backup(osp.join(tmpdir, source.uri), self.confirm)
   150                 except Exception, exc:
   149                 except Exception, ex:
   151                     print '-> error trying to backup [%s]' % exc
   150                     print '-> error trying to backup %s [%s]' % (source.uri, ex)
   152                     if not self.confirm('Continue anyway?', default='n'):
   151                     if not self.confirm('Continue anyway?', default='n'):
   153                         raise SystemExit(1)
   152                         raise SystemExit(1)
   154                     else:
   153                     else:
   155                         break
   154                         break
   156             else:
   155             else:
   157                 bkup = tarfile.open(backupfile, 'w|gz')
   156                 bkup = tarfile.open(backupfile, 'w|gz')
   158                 for filename in os.listdir(tmpdir):
   157                 for filename in os.listdir(tmpdir):
   159                     bkup.add(osp.join(tmpdir,filename), filename)
   158                     bkup.add(osp.join(tmpdir, filename), filename)
   160                 bkup.close()
   159                 bkup.close()
   161                 # call hooks
   160                 # call hooks
   162                 repo.hm.call_hooks('server_backup', repo=repo, timestamp=timestamp)
   161                 repo.hm.call_hooks('server_backup', repo=repo, timestamp=timestamp)
   163                 # done
   162                 # done
   164                 print '-> backup file',  backupfile
   163                 print '-> backup file',  backupfile
   168     def restore_database(self, backupfile, drop=True, systemonly=True,
   167     def restore_database(self, backupfile, drop=True, systemonly=True,
   169                          askconfirm=True):
   168                          askconfirm=True):
   170         # check
   169         # check
   171         if not osp.exists(backupfile):
   170         if not osp.exists(backupfile):
   172             raise Exception("Backup file %s doesn't exist" % backupfile)
   171             raise Exception("Backup file %s doesn't exist" % backupfile)
   173             return
       
   174         if askconfirm and not self.confirm('Restore %s database from %s ?'
   172         if askconfirm and not self.confirm('Restore %s database from %s ?'
   175                                            % (self.config.appid, backupfile)):
   173                                            % (self.config.appid, backupfile)):
   176             return
   174             return
   177         # unpack backup
   175         # unpack backup
   178         tmpdir = tempfile.mkdtemp()
   176         tmpdir = tempfile.mkdtemp()
   179         try:
   177         try:
   180             bkup = tarfile.open(backupfile, 'r|gz')
   178             bkup = tarfile.open(backupfile, 'r|gz')
   181         except tarfile.ReadError:
   179         except tarfile.ReadError:
   182             # assume restoring old backup
   180             # assume restoring old backup
   183             shutil.copy(backupfile, osp.join(tmpdir, 'system'))  
   181             shutil.copy(backupfile, osp.join(tmpdir, 'system'))
   184         else:
   182         else:
   185             for name in bkup.getnames():
   183             for name in bkup.getnames():
   186                 if name[0] in '/.':
   184                 if name[0] in '/.':
   187                     raise Exception('Security check failed, path starts with "/" or "."')
   185                     raise Exception('Security check failed, path starts with "/" or "."')
   188             bkup.close() # XXX seek error if not close+open !?!
   186             bkup.close() # XXX seek error if not close+open !?!
   189             bkup = tarfile.open(backupfile, 'r|gz')
   187             bkup = tarfile.open(backupfile, 'r|gz')
   190             bkup.extractall(path=tmpdir)
   188             bkup.extractall(path=tmpdir)
   191             bkup.close()
   189             bkup.close()
   192 
       
   193         self.config.open_connections_pools = False
   190         self.config.open_connections_pools = False
   194         repo = self.repo_connect()
   191         repo = self.repo_connect()
   195         for source in repo.sources:
   192         for source in repo.sources:
   196             if systemonly and source.uri != 'system':
   193             if systemonly and source.uri != 'system':
   197                 continue
   194                 continue
   198             try:
   195             try:
   199                 source.restore(osp.join(tmpdir, source.uri), self.confirm, drop)
   196                 source.restore(osp.join(tmpdir, source.uri), self.confirm, drop)
   200             except Exception, exc:
   197             except Exception, exc:
   201                 print '-> error trying to restore [%s]' % exc
   198                 print '-> error trying to restore %s [%s]' % (source.uri, exc)
   202                 if not self.confirm('Continue anyway?', default='n'):
   199                 if not self.confirm('Continue anyway?', default='n'):
   203                     raise SystemExit(1)
   200                     raise SystemExit(1)
   204         shutil.rmtree(tmpdir)
   201         shutil.rmtree(tmpdir)
   205         # call hooks
   202         # call hooks
   206         repo.open_connections_pools()
   203         repo.open_connections_pools()
   219                 pwd = sourcescfg['admin']['password']
   216                 pwd = sourcescfg['admin']['password']
   220             except KeyError:
   217             except KeyError:
   221                 login, pwd = manager_userpasswd()
   218                 login, pwd = manager_userpasswd()
   222             while True:
   219             while True:
   223                 try:
   220                 try:
   224                     self._cnx = repo_connect(self.repo, login, pwd)
   221                     self._cnx = repo_connect(self.repo, login, password=pwd)
   225                     if not 'managers' in self._cnx.user(self.session).groups:
   222                     if not 'managers' in self._cnx.user(self.session).groups:
   226                         print 'migration need an account in the managers group'
   223                         print 'migration need an account in the managers group'
   227                     else:
   224                     else:
   228                         break
   225                         break
   229                 except AuthenticationError:
   226                 except AuthenticationError:
   261 
   258 
   262     @cached
   259     @cached
   263     def _create_context(self):
   260     def _create_context(self):
   264         """return a dictionary to use as migration script execution context"""
   261         """return a dictionary to use as migration script execution context"""
   265         context = super(ServerMigrationHelper, self)._create_context()
   262         context = super(ServerMigrationHelper, self)._create_context()
   266         context.update({'checkpoint': self.checkpoint,
   263         context.update({'commit': self.checkpoint,
       
   264                         'rollback': self.rollback,
       
   265                         'checkpoint': deprecated('[3.6] use commit')(self.checkpoint),
   267                         'sql': self.sqlexec,
   266                         'sql': self.sqlexec,
   268                         'rql': self.rqlexec,
   267                         'rql': self.rqlexec,
   269                         'rqliter': self.rqliter,
   268                         'rqliter': self.rqliter,
   270                         'schema': self.repo.get_schema(),
   269                         'schema': self.repo.get_schema(),
   271                         'cnx': self.cnx,
   270                         'cnx': self.cnx,
   272                         'fsschema': self.fs_schema,
   271                         'fsschema': self.fs_schema,
   273                         'session' : self.session,
   272                         'session' : self.session,
   274                         'repo' : self.repo,
   273                         'repo' : self.repo,
   275                         'synchronize_schema': deprecated()(self.cmd_sync_schema_props_perms),
   274                         'synchronize_schema': deprecated()(self.cmd_sync_schema_props_perms), # 3.4
   276                         'synchronize_eschema': deprecated()(self.cmd_sync_schema_props_perms),
   275                         'synchronize_eschema': deprecated()(self.cmd_sync_schema_props_perms), # 3.4
   277                         'synchronize_rschema': deprecated()(self.cmd_sync_schema_props_perms),
   276                         'synchronize_rschema': deprecated()(self.cmd_sync_schema_props_perms), # 3.4
   278                         })
   277                         })
   279         return context
   278         return context
   280 
   279 
   281     @cached
   280     @cached
   282     def group_mapping(self):
   281     def group_mapping(self):
   289             apc = osp.join(cubepath, 'migration', '%s.py' % event)
   288             apc = osp.join(cubepath, 'migration', '%s.py' % event)
   290         else:
   289         else:
   291             apc = osp.join(self.config.migration_scripts_dir(), '%s.py' % event)
   290             apc = osp.join(self.config.migration_scripts_dir(), '%s.py' % event)
   292         if osp.exists(apc):
   291         if osp.exists(apc):
   293             if self.config.free_wheel:
   292             if self.config.free_wheel:
   294                 from cubicweb.server.hooks import setowner_after_add_entity
   293                 self.cmd_deactivate_verification_hooks()
   295                 self.repo.hm.unregister_hook(setowner_after_add_entity,
       
   296                                              'after_add_entity', '')
       
   297                 self.deactivate_verification_hooks()
       
   298             self.info('executing %s', apc)
   294             self.info('executing %s', apc)
   299             confirm = self.confirm
   295             confirm = self.confirm
   300             execscript_confirm = self.execscript_confirm
   296             execscript_confirm = self.execscript_confirm
   301             self.confirm = yes
   297             self.confirm = yes
   302             self.execscript_confirm = yes
   298             self.execscript_confirm = yes
   304                 return self.cmd_process_script(apc, funcname, *args, **kwargs)
   300                 return self.cmd_process_script(apc, funcname, *args, **kwargs)
   305             finally:
   301             finally:
   306                 self.confirm = confirm
   302                 self.confirm = confirm
   307                 self.execscript_confirm = execscript_confirm
   303                 self.execscript_confirm = execscript_confirm
   308                 if self.config.free_wheel:
   304                 if self.config.free_wheel:
   309                     self.repo.hm.register_hook(setowner_after_add_entity,
   305                     self.cmd_reactivate_verification_hooks()
   310                                                'after_add_entity', '')
       
   311                     self.reactivate_verification_hooks()
       
   312 
   306 
   313     def install_custom_sql_scripts(self, directory, driver):
   307     def install_custom_sql_scripts(self, directory, driver):
   314         self.session.set_pool() # ensure pool is set
   308         self.session.set_pool() # ensure pool is set
   315         for fpath in glob(osp.join(directory, '*.sql.%s' % driver)):
   309         for fpath in glob(osp.join(directory, '*.sql.%s' % driver)):
   316             newname = osp.basename(fpath).replace('.sql.%s' % driver,
   310             newname = osp.basename(fpath).replace('.sql.%s' % driver,
   325             sqlexec(open(fpath).read(), self.session.system_sql, False,
   319             sqlexec(open(fpath).read(), self.session.system_sql, False,
   326                     delimiter=';;')
   320                     delimiter=';;')
   327 
   321 
   328     # schema synchronization internals ########################################
   322     # schema synchronization internals ########################################
   329 
   323 
   330     def _synchronize_permissions(self, ertype):
   324     def _synchronize_permissions(self, erschema, teid):
   331         """permission synchronization for an entity or relation type"""
   325         """permission synchronization for an entity or relation type"""
   332         if ertype in VIRTUAL_RTYPES:
   326         assert teid, erschema
   333             return
   327         if 'update' in erschema.ACTIONS or erschema.final:
   334         newrschema = self.fs_schema[ertype]
       
   335         teid = self.repo.schema[ertype].eid
       
   336         if 'update' in newrschema.ACTIONS or newrschema.final:
       
   337             # entity type
   328             # entity type
   338             exprtype = u'ERQLExpression'
   329             exprtype = u'ERQLExpression'
   339         else:
   330         else:
   340             # relation type
   331             # relation type
   341             exprtype = u'RRQLExpression'
   332             exprtype = u'RRQLExpression'
   342         assert teid, ertype
       
   343         gm = self.group_mapping()
   333         gm = self.group_mapping()
   344         confirm = self.verbosity >= 2
   334         confirm = self.verbosity >= 2
   345         # * remove possibly deprecated permission (eg in the persistent schema
   335         # * remove possibly deprecated permission (eg in the persistent schema
   346         #   but not in the new schema)
   336         #   but not in the new schema)
   347         # * synchronize existing expressions
   337         # * synchronize existing expressions
   348         # * add new groups/expressions
   338         # * add new groups/expressions
   349         for action in newrschema.ACTIONS:
   339         for action in erschema.ACTIONS:
   350             perm = '%s_permission' % action
   340             perm = '%s_permission' % action
   351             # handle groups
   341             # handle groups
   352             newgroups = list(newrschema.get_groups(action))
   342             newgroups = list(erschema.get_groups(action))
   353             for geid, gname in self.rqlexec('Any G, GN WHERE T %s G, G name GN, '
   343             for geid, gname in self.rqlexec('Any G, GN WHERE T %s G, G name GN, '
   354                                             'T eid %%(x)s' % perm, {'x': teid}, 'x',
   344                                             'T eid %%(x)s' % perm, {'x': teid}, 'x',
   355                                             ask_confirm=False):
   345                                             ask_confirm=False):
   356                 if not gname in newgroups:
   346                 if not gname in newgroups:
   357                     if not confirm or self.confirm('remove %s permission of %s to %s?'
   347                     if not confirm or self.confirm('Remove %s permission of %s to %s?'
   358                                                    % (action, ertype, gname)):
   348                                                    % (action, erschema, gname)):
   359                         self.rqlexec('DELETE T %s G WHERE G eid %%(x)s, T eid %s'
   349                         self.rqlexec('DELETE T %s G WHERE G eid %%(x)s, T eid %s'
   360                                      % (perm, teid),
   350                                      % (perm, teid),
   361                                      {'x': geid}, 'x', ask_confirm=False)
   351                                      {'x': geid}, 'x', ask_confirm=False)
   362                 else:
   352                 else:
   363                     newgroups.remove(gname)
   353                     newgroups.remove(gname)
   364             for gname in newgroups:
   354             for gname in newgroups:
   365                 if not confirm or self.confirm('grant %s permission of %s to %s?'
   355                 if not confirm or self.confirm('Grant %s permission of %s to %s?'
   366                                                % (action, ertype, gname)):
   356                                                % (action, erschema, gname)):
   367                     self.rqlexec('SET T %s G WHERE G eid %%(x)s, T eid %s'
   357                     self.rqlexec('SET T %s G WHERE G eid %%(x)s, T eid %s'
   368                                  % (perm, teid),
   358                                  % (perm, teid),
   369                                  {'x': gm[gname]}, 'x', ask_confirm=False)
   359                                  {'x': gm[gname]}, 'x', ask_confirm=False)
   370             # handle rql expressions
   360             # handle rql expressions
   371             newexprs = dict((expr.expression, expr) for expr in newrschema.get_rqlexprs(action))
   361             newexprs = dict((expr.expression, expr) for expr in erschema.get_rqlexprs(action))
   372             for expreid, expression in self.rqlexec('Any E, EX WHERE T %s E, E expression EX, '
   362             for expreid, expression in self.rqlexec('Any E, EX WHERE T %s E, E expression EX, '
   373                                                     'T eid %s' % (perm, teid),
   363                                                     'T eid %s' % (perm, teid),
   374                                                     ask_confirm=False):
   364                                                     ask_confirm=False):
   375                 if not expression in newexprs:
   365                 if not expression in newexprs:
   376                     if not confirm or self.confirm('remove %s expression for %s permission of %s?'
   366                     if not confirm or self.confirm('Remove %s expression for %s permission of %s?'
   377                                                    % (expression, action, ertype)):
   367                                                    % (expression, action, erschema)):
   378                         # deleting the relation will delete the expression entity
   368                         # deleting the relation will delete the expression entity
   379                         self.rqlexec('DELETE T %s E WHERE E eid %%(x)s, T eid %s'
   369                         self.rqlexec('DELETE T %s E WHERE E eid %%(x)s, T eid %s'
   380                                      % (perm, teid),
   370                                      % (perm, teid),
   381                                      {'x': expreid}, 'x', ask_confirm=False)
   371                                      {'x': expreid}, 'x', ask_confirm=False)
   382                 else:
   372                 else:
   383                     newexprs.pop(expression)
   373                     newexprs.pop(expression)
   384             for expression in newexprs.values():
   374             for expression in newexprs.values():
   385                 expr = expression.expression
   375                 expr = expression.expression
   386                 if not confirm or self.confirm('add %s expression for %s permission of %s?'
   376                 if not confirm or self.confirm('Add %s expression for %s permission of %s?'
   387                                                % (expr, action, ertype)):
   377                                                % (expr, action, erschema)):
   388                     self.rqlexec('INSERT RQLExpression X: X exprtype %%(exprtype)s, '
   378                     self.rqlexec('INSERT RQLExpression X: X exprtype %%(exprtype)s, '
   389                                  'X expression %%(expr)s, X mainvars %%(vars)s, T %s X '
   379                                  'X expression %%(expr)s, X mainvars %%(vars)s, T %s X '
   390                                  'WHERE T eid %%(x)s' % perm,
   380                                  'WHERE T eid %%(x)s' % perm,
   391                                  {'expr': expr, 'exprtype': exprtype,
   381                                  {'expr': expr, 'exprtype': exprtype,
   392                                   'vars': expression.mainvars, 'x': teid}, 'x',
   382                                   'vars': expression.mainvars, 'x': teid}, 'x',
   393                                  ask_confirm=False)
   383                                  ask_confirm=False)
   394 
   384 
   395     def _synchronize_rschema(self, rtype, syncrdefs=True, syncperms=True):
   385     def _synchronize_rschema(self, rtype, syncrdefs=True, syncperms=True, syncprops=True):
   396         """synchronize properties of the persistent relation schema against its
   386         """synchronize properties of the persistent relation schema against its
   397         current definition:
   387         current definition:
   398 
   388 
   399         * description
   389         * description
   400         * symetric, meta
   390         * symmetric, meta
   401         * inlined
   391         * inlined
   402         * relation definitions if `syncrdefs`
   392         * relation definitions if `syncrdefs`
   403         * permissions if `syncperms`
   393         * permissions if `syncperms`
   404 
   394 
   405         physical schema changes should be handled by repository's schema hooks
   395         physical schema changes should be handled by repository's schema hooks
   407         rtype = str(rtype)
   397         rtype = str(rtype)
   408         if rtype in self._synchronized:
   398         if rtype in self._synchronized:
   409             return
   399             return
   410         self._synchronized.add(rtype)
   400         self._synchronized.add(rtype)
   411         rschema = self.fs_schema.rschema(rtype)
   401         rschema = self.fs_schema.rschema(rtype)
   412         self.rqlexecall(ss.updaterschema2rql(rschema),
   402         if syncprops:
   413                         ask_confirm=self.verbosity>=2)
   403             self.rqlexecall(ss.updaterschema2rql(rschema),
   414         reporschema = self.repo.schema.rschema(rtype)
   404                             ask_confirm=self.verbosity>=2)
   415         if syncrdefs:
   405         if syncrdefs:
   416             for subj, obj in rschema.iter_rdefs():
   406             reporschema = self.repo.schema.rschema(rtype)
   417                 if not reporschema.has_rdef(subj, obj):
   407             for subj, obj in rschema.rdefs:
       
   408                 if (subj, obj) not in reporschema.rdefs:
   418                     continue
   409                     continue
   419                 self._synchronize_rdef_schema(subj, rschema, obj)
   410                 self._synchronize_rdef_schema(subj, rschema, obj,
   420         if syncperms:
   411                                               syncprops=syncprops,
   421             self._synchronize_permissions(rtype)
   412                                               syncperms=syncperms)
   422 
   413 
   423     def _synchronize_eschema(self, etype, syncperms=True):
   414     def _synchronize_eschema(self, etype, syncperms=True):
   424         """synchronize properties of the persistent entity schema against
   415         """synchronize properties of the persistent entity schema against
   425         its current definition:
   416         its current definition:
   426 
   417 
   462             self._synchronize_rschema(rschema, syncperms=syncperms,
   453             self._synchronize_rschema(rschema, syncperms=syncperms,
   463                                       syncrdefs=False)
   454                                       syncrdefs=False)
   464             reporschema = self.repo.schema.rschema(rschema)
   455             reporschema = self.repo.schema.rschema(rschema)
   465             for subj in subjtypes:
   456             for subj in subjtypes:
   466                 for obj in objtypes:
   457                 for obj in objtypes:
   467                     if not reporschema.has_rdef(subj, obj):
   458                     if (subj, obj) not in reporschema.rdefs:
   468                         continue
   459                         continue
   469                     self._synchronize_rdef_schema(subj, rschema, obj)
   460                     self._synchronize_rdef_schema(subj, rschema, obj)
   470         if syncperms:
   461         if syncperms:
   471             self._synchronize_permissions(etype)
   462             self._synchronize_permissions(eschema, repoeschema.eid)
   472 
   463 
   473     def _synchronize_rdef_schema(self, subjtype, rtype, objtype):
   464     def _synchronize_rdef_schema(self, subjtype, rtype, objtype,
       
   465                                  syncperms=True, syncprops=True):
   474         """synchronize properties of the persistent relation definition schema
   466         """synchronize properties of the persistent relation definition schema
   475         against its current definition:
   467         against its current definition:
   476         * order and other properties
   468         * order and other properties
   477         * constraints
   469         * constraints
   478         """
   470         """
   480         rschema = self.fs_schema.rschema(rtype)
   472         rschema = self.fs_schema.rschema(rtype)
   481         reporschema = self.repo.schema.rschema(rschema)
   473         reporschema = self.repo.schema.rschema(rschema)
   482         if (subjtype, rschema, objtype) in self._synchronized:
   474         if (subjtype, rschema, objtype) in self._synchronized:
   483             return
   475             return
   484         self._synchronized.add((subjtype, rschema, objtype))
   476         self._synchronized.add((subjtype, rschema, objtype))
   485         if rschema.symetric:
   477         if rschema.symmetric:
   486             self._synchronized.add((objtype, rschema, subjtype))
   478             self._synchronized.add((objtype, rschema, subjtype))
       
   479         rdef = rschema.rdef(subjtype, objtype)
       
   480         if rdef.infered:
       
   481             return # don't try to synchronize infered relation defs
       
   482         repordef = reporschema.rdef(subjtype, objtype)
   487         confirm = self.verbosity >= 2
   483         confirm = self.verbosity >= 2
   488         # properties
   484         if syncprops:
   489         self.rqlexecall(ss.updaterdef2rql(rschema, subjtype, objtype),
   485             # properties
   490                         ask_confirm=confirm)
   486             self.rqlexecall(ss.updaterdef2rql(rschema, subjtype, objtype),
   491         # constraints
   487                             ask_confirm=confirm)
   492         newconstraints = list(rschema.rproperty(subjtype, objtype, 'constraints'))
   488             # constraints
   493         # 1. remove old constraints and update constraints of the same type
   489             newconstraints = list(rdef.constraints)
   494         # NOTE: don't use rschema.constraint_by_type because it may be
   490             # 1. remove old constraints and update constraints of the same type
   495         #       out of sync with newconstraints when multiple
   491             # NOTE: don't use rschema.constraint_by_type because it may be
   496         #       constraints of the same type are used
   492             #       out of sync with newconstraints when multiple
   497         for cstr in reporschema.rproperty(subjtype, objtype, 'constraints'):
   493             #       constraints of the same type are used
       
   494             for cstr in repordef.constraints:
       
   495                 for newcstr in newconstraints:
       
   496                     if newcstr.type() == cstr.type():
       
   497                         break
       
   498                 else:
       
   499                     newcstr = None
       
   500                 if newcstr is None:
       
   501                     self.rqlexec('DELETE X constrained_by C WHERE C eid %(x)s',
       
   502                                  {'x': cstr.eid}, 'x',
       
   503                                  ask_confirm=confirm)
       
   504                 else:
       
   505                     newconstraints.remove(newcstr)
       
   506                     value = unicode(newcstr.serialize())
       
   507                     if value != unicode(cstr.serialize()):
       
   508                         self.rqlexec('SET X value %(v)s WHERE X eid %(x)s',
       
   509                                      {'x': cstr.eid, 'v': value}, 'x',
       
   510                                      ask_confirm=confirm)
       
   511             # 2. add new constraints
   498             for newcstr in newconstraints:
   512             for newcstr in newconstraints:
   499                 if newcstr.type() == cstr.type():
   513                 self.rqlexecall(ss.constraint2rql(rschema, subjtype, objtype,
   500                     break
   514                                                   newcstr),
   501             else:
   515                                 ask_confirm=confirm)
   502                 newcstr = None
   516         if syncperms and not rschema in VIRTUAL_RTYPES:
   503             if newcstr is None:
   517             self._synchronize_permissions(rdef, repordef.eid)
   504                 self.rqlexec('DELETE X constrained_by C WHERE C eid %(x)s',
       
   505                              {'x': cstr.eid}, 'x',
       
   506                              ask_confirm=confirm)
       
   507                 self.rqlexec('DELETE CWConstraint C WHERE C eid %(x)s',
       
   508                              {'x': cstr.eid}, 'x',
       
   509                              ask_confirm=confirm)
       
   510             else:
       
   511                 newconstraints.remove(newcstr)
       
   512                 values = {'x': cstr.eid,
       
   513                           'v': unicode(newcstr.serialize())}
       
   514                 self.rqlexec('SET X value %(v)s WHERE X eid %(x)s',
       
   515                              values, 'x', ask_confirm=confirm)
       
   516         # 2. add new constraints
       
   517         for newcstr in newconstraints:
       
   518             self.rqlexecall(ss.constraint2rql(rschema, subjtype, objtype,
       
   519                                               newcstr),
       
   520                             ask_confirm=confirm)
       
   521 
   518 
   522     # base actions ############################################################
   519     # base actions ############################################################
   523 
   520 
   524     def checkpoint(self):
   521     def checkpoint(self, ask_confirm=True):
   525         """checkpoint action"""
   522         """checkpoint action"""
   526         if self.confirm('commit now ?', shell=False):
   523         if not ask_confirm or self.confirm('Commit now ?', shell=False):
   527             self.commit()
   524             self.commit()
   528 
   525 
   529     def cmd_add_cube(self, cube, update_database=True):
   526     def cmd_add_cube(self, cube, update_database=True):
   530         self.cmd_add_cubes( (cube,), update_database)
   527         self.cmd_add_cubes( (cube,), update_database)
   531 
   528 
   577             self.cmd_add_entity_type(eschema.type)
   574             self.cmd_add_entity_type(eschema.type)
   578             new.add(eschema.type)
   575             new.add(eschema.type)
   579         # check if attributes has been added to existing entities
   576         # check if attributes has been added to existing entities
   580         for rschema in newcubes_schema.relations():
   577         for rschema in newcubes_schema.relations():
   581             existingschema = self.repo.schema.rschema(rschema.type)
   578             existingschema = self.repo.schema.rschema(rschema.type)
   582             for (fromtype, totype) in rschema.iter_rdefs():
   579             for (fromtype, totype) in rschema.rdefs:
   583                 if existingschema.has_rdef(fromtype, totype):
   580                 if (fromtype, totype) in existingschema.rdefs:
   584                     continue
   581                     continue
   585                 # check we should actually add the relation definition
   582                 # check we should actually add the relation definition
   586                 if not (fromtype in new or totype in new or rschema in new):
   583                 if not (fromtype in new or totype in new or rschema in new):
   587                     continue
   584                     continue
   588                 self.cmd_add_relation_definition(str(fromtype), rschema.type,
   585                 self.cmd_add_relation_definition(str(fromtype), rschema.type,
   614             self.cmd_drop_entity_type(eschema.type)
   611             self.cmd_drop_entity_type(eschema.type)
   615         for rschema in fsschema.relations():
   612         for rschema in fsschema.relations():
   616             if rschema in removedcubes_schema and rschema in reposchema:
   613             if rschema in removedcubes_schema and rschema in reposchema:
   617                 # check if attributes/relations has been added to entities from
   614                 # check if attributes/relations has been added to entities from
   618                 # other cubes
   615                 # other cubes
   619                 for fromtype, totype in rschema.iter_rdefs():
   616                 for fromtype, totype in rschema.rdefs:
   620                     if not removedcubes_schema[rschema.type].has_rdef(fromtype, totype) and \
   617                     if (fromtype, totype) not in removedcubes_schema[rschema.type].rdefs and \
   621                            reposchema[rschema.type].has_rdef(fromtype, totype):
   618                            (fromtype, totype) in reposchema[rschema.type].rdefs:
   622                         self.cmd_drop_relation_definition(
   619                         self.cmd_drop_relation_definition(
   623                             str(fromtype), rschema.type, str(totype))
   620                             str(fromtype), rschema.type, str(totype))
   624         # execute post-remove files
   621         # execute post-remove files
   625         for pack in reversed(removedcubes):
   622         for pack in reversed(removedcubes):
   626             self.exec_event_script('postremove', self.config.cube_dir(pack))
   623             self.exec_event_script('postremove', self.config.cube_dir(pack))
   657         # have to commit this first step anyway to get the definition
   654         # have to commit this first step anyway to get the definition
   658         # actually in the schema
   655         # actually in the schema
   659         self.cmd_add_attribute(etype, newname, attrtype, commit=True)
   656         self.cmd_add_attribute(etype, newname, attrtype, commit=True)
   660         # skipp NULL values if the attribute is required
   657         # skipp NULL values if the attribute is required
   661         rql = 'SET X %s VAL WHERE X is %s, X %s VAL' % (newname, etype, oldname)
   658         rql = 'SET X %s VAL WHERE X is %s, X %s VAL' % (newname, etype, oldname)
   662         card = eschema.rproperty(newname, 'cardinality')[0]
   659         card = eschema.rdef(newname).cardinality[0]
   663         if card == '1':
   660         if card == '1':
   664             rql += ', NOT X %s NULL' % oldname
   661             rql += ', NOT X %s NULL' % oldname
   665         self.rqlexec(rql, ask_confirm=self.verbosity>=2)
   662         self.rqlexec(rql, ask_confirm=self.verbosity>=2)
   666         # XXX if both attributes fulltext indexed, should skip fti rebuild
   663         # XXX if both attributes fulltext indexed, should skip fti rebuild
   667         # XXX if old attribute was fti indexed but not the new one old value
   664         # XXX if old attribute was fti indexed but not the new one old value
   683             if eschema.final:
   680             if eschema.final:
   684                 instschema.del_entity_type(etype)
   681                 instschema.del_entity_type(etype)
   685         else:
   682         else:
   686             eschema = self.fs_schema.eschema(etype)
   683             eschema = self.fs_schema.eschema(etype)
   687         confirm = self.verbosity >= 2
   684         confirm = self.verbosity >= 2
       
   685         groupmap = self.group_mapping()
   688         # register the entity into CWEType
   686         # register the entity into CWEType
   689         self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm)
   687         self.rqlexecall(ss.eschema2rql(eschema, groupmap), ask_confirm=confirm)
   690         # add specializes relation if needed
   688         # add specializes relation if needed
   691         self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
   689         self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
   692         # register groups / permissions for the entity
       
   693         self.rqlexecall(ss.erperms2rql(eschema, self.group_mapping()),
       
   694                         ask_confirm=confirm)
       
   695         # register entity's attributes
   690         # register entity's attributes
   696         for rschema, attrschema in eschema.attribute_definitions():
   691         for rschema, attrschema in eschema.attribute_definitions():
   697             # ignore those meta relations, they will be automatically added
   692             # ignore those meta relations, they will be automatically added
   698             if rschema.type in META_RTYPES:
   693             if rschema.type in META_RTYPES:
   699                 continue
   694                 continue
   700             if not rschema.type in instschema:
   695             if not rschema.type in instschema:
   701                 # need to add the relation type and to commit to get it
   696                 # need to add the relation type and to commit to get it
   702                 # actually in the schema
   697                 # actually in the schema
   703                 self.cmd_add_relation_type(rschema.type, False, commit=True)
   698                 self.cmd_add_relation_type(rschema.type, False, commit=True)
   704             # register relation definition
   699             # register relation definition
   705             self.rqlexecall(ss.rdef2rql(rschema, etype, attrschema.type),
   700             self.rqlexecall(ss.rdef2rql(rschema, etype, attrschema.type,
       
   701                                         groupmap=groupmap),
   706                             ask_confirm=confirm)
   702                             ask_confirm=confirm)
   707         # take care to newly introduced base class
   703         # take care to newly introduced base class
   708         # XXX some part of this should probably be under the "if auto" block
   704         # XXX some part of this should probably be under the "if auto" block
   709         for spschema in eschema.specialized_by(recursive=False):
   705         for spschema in eschema.specialized_by(recursive=False):
   710             try:
   706             try:
   712             except KeyError:
   708             except KeyError:
   713                 # specialized entity type not in schema, ignore
   709                 # specialized entity type not in schema, ignore
   714                 continue
   710                 continue
   715             if instspschema.specializes() != eschema:
   711             if instspschema.specializes() != eschema:
   716                 self.rqlexec('SET D specializes P WHERE D eid %(d)s, P name %(pn)s',
   712                 self.rqlexec('SET D specializes P WHERE D eid %(d)s, P name %(pn)s',
   717                               {'d': instspschema.eid,
   713                              {'d': instspschema.eid,
   718                                'pn': eschema.type}, ask_confirm=confirm)
   714                               'pn': eschema.type}, ask_confirm=confirm)
   719                 for rschema, tschemas, role in spschema.relation_definitions(True):
   715                 for rschema, tschemas, role in spschema.relation_definitions(True):
   720                     for tschema in tschemas:
   716                     for tschema in tschemas:
   721                         if not tschema in instschema:
   717                         if not tschema in instschema:
   722                             continue
   718                             continue
   723                         if role == 'subject':
   719                         if role == 'subject':
   729                                 # simply skip here
   725                                 # simply skip here
   730                                 continue
   726                                 continue
   731                         elif role == 'object':
   727                         elif role == 'object':
   732                             subjschema = tschema
   728                             subjschema = tschema
   733                             objschema = spschema
   729                             objschema = spschema
   734                         if (rschema.rproperty(subjschema, objschema, 'infered')
   730                         if (rschema.rdef(subjschema, objschema).infered
   735                             or (instschema.has_relation(rschema) and
   731                             or (instschema.has_relation(rschema) and
   736                                 instschema[rschema].has_rdef(subjschema, objschema))):
   732                                 (subjschema, objschema) in instschema[rschema].rdefs)):
   737                                 continue
   733                             continue
   738                         self.cmd_add_relation_definition(
   734                         self.cmd_add_relation_definition(
   739                             subjschema.type, rschema.type, objschema.type)
   735                             subjschema.type, rschema.type, objschema.type)
   740         if auto:
   736         if auto:
   741             # we have commit here to get relation types actually in the schema
   737             # we have commit here to get relation types actually in the schema
   742             self.commit()
   738             self.commit()
   758                         # actually in the schema
   754                         # actually in the schema
   759                         added.append(rschema.type)
   755                         added.append(rschema.type)
   760                         self.cmd_add_relation_type(rschema.type, False, commit=True)
   756                         self.cmd_add_relation_type(rschema.type, False, commit=True)
   761                         rtypeadded = True
   757                         rtypeadded = True
   762                     # register relation definition
   758                     # register relation definition
   763                     # remember this two avoid adding twice non symetric relation
   759                     # remember this two avoid adding twice non symmetric relation
   764                     # such as "Emailthread forked_from Emailthread"
   760                     # such as "Emailthread forked_from Emailthread"
   765                     added.append((etype, rschema.type, targettype))
   761                     added.append((etype, rschema.type, targettype))
   766                     self.rqlexecall(ss.rdef2rql(rschema, etype, targettype),
   762                     self.rqlexecall(ss.rdef2rql(rschema, etype, targettype,
       
   763                                                 groupmap=groupmap),
   767                                     ask_confirm=confirm)
   764                                     ask_confirm=confirm)
   768             for rschema in eschema.object_relations():
   765             for rschema in eschema.object_relations():
   769                 rtypeadded = rschema.type in instschema or rschema.type in added
   766                 rtypeadded = rschema.type in instschema or rschema.type in added
   770                 for targetschema in rschema.subjects(etype):
   767                 for targetschema in rschema.subjects(etype):
   771                     # ignore relations where the targeted type is not in the
   768                     # ignore relations where the targeted type is not in the
   781                         self.cmd_add_relation_type(rschema.type, False, commit=True)
   778                         self.cmd_add_relation_type(rschema.type, False, commit=True)
   782                         rtypeadded = True
   779                         rtypeadded = True
   783                     elif (targettype, rschema.type, etype) in added:
   780                     elif (targettype, rschema.type, etype) in added:
   784                         continue
   781                         continue
   785                     # register relation definition
   782                     # register relation definition
   786                     self.rqlexecall(ss.rdef2rql(rschema, targettype, etype),
   783                     self.rqlexecall(ss.rdef2rql(rschema, targettype, etype,
       
   784                                                 groupmap=groupmap),
   787                                     ask_confirm=confirm)
   785                                     ask_confirm=confirm)
   788         if commit:
   786         if commit:
   789             self.commit()
   787             self.commit()
   790 
   788 
   791     def cmd_drop_entity_type(self, etype, commit=True):
   789     def cmd_drop_entity_type(self, etype, commit=True):
   826         rschema = self.fs_schema.rschema(rtype)
   824         rschema = self.fs_schema.rschema(rtype)
   827         # register the relation into CWRType and insert necessary relation
   825         # register the relation into CWRType and insert necessary relation
   828         # definitions
   826         # definitions
   829         self.rqlexecall(ss.rschema2rql(rschema, addrdef=False),
   827         self.rqlexecall(ss.rschema2rql(rschema, addrdef=False),
   830                         ask_confirm=self.verbosity>=2)
   828                         ask_confirm=self.verbosity>=2)
   831         # register groups / permissions for the relation
       
   832         self.rqlexecall(ss.erperms2rql(rschema, self.group_mapping()),
       
   833                         ask_confirm=self.verbosity>=2)
       
   834         if addrdef:
   829         if addrdef:
   835             self.commit()
   830             self.commit()
   836             self.rqlexecall(ss.rdef2rql(rschema),
   831             self.rqlexecall(ss.rdef2rql(rschema, groupmap=self.group_mapping()),
   837                             ask_confirm=self.verbosity>=2)
   832                             ask_confirm=self.verbosity>=2)
   838             if rtype in META_RTYPES:
   833             if rtype in META_RTYPES:
   839                 # if the relation is in META_RTYPES, ensure we're adding it for
   834                 # if the relation is in META_RTYPES, ensure we're adding it for
   840                 # all entity types *in the persistent schema*, not only those in
   835                 # all entity types *in the persistent schema*, not only those in
   841                 # the fs schema
   836                 # the fs schema
   846                         assert len(objtypes) == 1
   841                         assert len(objtypes) == 1
   847                         objtype = objtypes[0]
   842                         objtype = objtypes[0]
   848                         props = rschema.rproperties(
   843                         props = rschema.rproperties(
   849                             rschema.subjects(objtype)[0], objtype)
   844                             rschema.subjects(objtype)[0], objtype)
   850                         assert props
   845                         assert props
   851                         self.rqlexecall(ss.rdef2rql(rschema, etype, objtype, props),
   846                         self.rqlexecall(ss.rdef2rql(rschema, etype, objtype, props,
       
   847                                                     groupmap=self.group_mapping()),
   852                                         ask_confirm=self.verbosity>=2)
   848                                         ask_confirm=self.verbosity>=2)
   853 
   849 
   854         if commit:
   850         if commit:
   855             self.commit()
   851             self.commit()
   856 
   852 
   878         schema definition file
   874         schema definition file
   879         """
   875         """
   880         rschema = self.fs_schema.rschema(rtype)
   876         rschema = self.fs_schema.rschema(rtype)
   881         if not rtype in self.repo.schema:
   877         if not rtype in self.repo.schema:
   882             self.cmd_add_relation_type(rtype, addrdef=False, commit=True)
   878             self.cmd_add_relation_type(rtype, addrdef=False, commit=True)
   883         self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype),
   879         self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype,
       
   880                                     groupmap=self.group_mapping()),
   884                         ask_confirm=self.verbosity>=2)
   881                         ask_confirm=self.verbosity>=2)
   885         if commit:
   882         if commit:
   886             self.commit()
   883             self.commit()
   887 
   884 
   888     def cmd_drop_relation_definition(self, subjtype, rtype, objtype, commit=True):
   885     def cmd_drop_relation_definition(self, subjtype, rtype, objtype, commit=True):
   911         assert syncperms or syncprops, 'nothing to do'
   908         assert syncperms or syncprops, 'nothing to do'
   912         if ertype is not None:
   909         if ertype is not None:
   913             if isinstance(ertype, (tuple, list)):
   910             if isinstance(ertype, (tuple, list)):
   914                 assert len(ertype) == 3, 'not a relation definition'
   911                 assert len(ertype) == 3, 'not a relation definition'
   915                 assert syncprops, 'can\'t update permission for a relation definition'
   912                 assert syncprops, 'can\'t update permission for a relation definition'
   916                 self._synchronize_rdef_schema(*ertype)
   913                 self._synchronize_rdef_schema(ertype[0], ertype[1], ertype[2],
   917             elif syncprops:
   914                                               syncperms=syncperms,
       
   915                                               syncprops=syncprops)
       
   916             else:
   918                 erschema = self.repo.schema[ertype]
   917                 erschema = self.repo.schema[ertype]
   919                 if isinstance(erschema, CubicWebRelationSchema):
   918                 if isinstance(erschema, CubicWebRelationSchema):
   920                     self._synchronize_rschema(erschema, syncperms=syncperms,
   919                     self._synchronize_rschema(erschema, syncperms=syncperms,
       
   920                                               syncprops=syncprops,
   921                                               syncrdefs=syncrdefs)
   921                                               syncrdefs=syncrdefs)
       
   922                 elif syncprops:
       
   923                     self._synchronize_eschema(erschema, syncperms=syncperms)
   922                 else:
   924                 else:
   923                     self._synchronize_eschema(erschema, syncperms=syncperms)
   925                     self._synchronize_permissions(self.fs_schema[ertype], erschema.eid)
   924             else:
       
   925                 self._synchronize_permissions(ertype)
       
   926         else:
   926         else:
   927             for etype in self.repo.schema.entities():
   927             for etype in self.repo.schema.entities():
   928                 if syncprops:
   928                 if syncprops:
   929                     self._synchronize_eschema(etype, syncperms=syncperms)
   929                     self._synchronize_eschema(etype, syncperms=syncperms)
   930                 else:
   930                 else:
   931                     self._synchronize_permissions(etype)
   931                     self._synchronize_permissions(self.fs_schema[etype], etype.eid)
   932         if commit:
   932         if commit:
   933             self.commit()
   933             self.commit()
   934 
   934 
   935     def cmd_change_relation_props(self, subjtype, rtype, objtype,
   935     def cmd_change_relation_props(self, subjtype, rtype, objtype,
   936                                   commit=True, **kwargs):
   936                                   commit=True, **kwargs):
   963         if size is None any size constraint will be removed.
   963         if size is None any size constraint will be removed.
   964 
   964 
   965         you usually want to use sync_schema_props_perms instead.
   965         you usually want to use sync_schema_props_perms instead.
   966         """
   966         """
   967         oldvalue = None
   967         oldvalue = None
   968         for constr in self.repo.schema.eschema(etype).constraints(rtype):
   968         for constr in self.repo.schema.eschema(etype).rdef(rtype).constraints:
   969             if isinstance(constr, SizeConstraint):
   969             if isinstance(constr, SizeConstraint):
   970                 oldvalue = constr.max
   970                 oldvalue = constr.max
   971         if oldvalue == size:
   971         if oldvalue == size:
   972             return
   972             return
   973         if oldvalue is None and not size is None:
   973         if oldvalue is None and not size is None:
  1043                             {'et': etypes[0]})
  1043                             {'et': etypes[0]})
  1044         if rset:
  1044         if rset:
  1045             return rset.get_entity(0, 0)
  1045             return rset.get_entity(0, 0)
  1046         return self.cmd_add_workflow('%s workflow' % ';'.join(etypes), etypes)
  1046         return self.cmd_add_workflow('%s workflow' % ';'.join(etypes), etypes)
  1047 
  1047 
  1048     @deprecated('[3.5] use add_workflow and Workflow.add_state method')
  1048     @deprecated('[3.5] use add_workflow and Workflow.add_state method',
       
  1049                 stacklevel=3)
  1049     def cmd_add_state(self, name, stateof, initial=False, commit=False, **kwargs):
  1050     def cmd_add_state(self, name, stateof, initial=False, commit=False, **kwargs):
  1050         """method to ease workflow definition: add a state for one or more
  1051         """method to ease workflow definition: add a state for one or more
  1051         entity type(s)
  1052         entity type(s)
  1052         """
  1053         """
  1053         wf = self._get_or_create_wf(stateof)
  1054         wf = self._get_or_create_wf(stateof)
  1054         state = wf.add_state(name, initial, **kwargs)
  1055         state = wf.add_state(name, initial, **kwargs)
  1055         if commit:
  1056         if commit:
  1056             self.commit()
  1057             self.commit()
  1057         return state.eid
  1058         return state.eid
  1058 
  1059 
  1059     @deprecated('[3.5] use add_workflow and Workflow.add_transition method')
  1060     @deprecated('[3.5] use add_workflow and Workflow.add_transition method',
       
  1061                 stacklevel=3)
  1060     def cmd_add_transition(self, name, transitionof, fromstates, tostate,
  1062     def cmd_add_transition(self, name, transitionof, fromstates, tostate,
  1061                            requiredgroups=(), conditions=(), commit=False, **kwargs):
  1063                            requiredgroups=(), conditions=(), commit=False, **kwargs):
  1062         """method to ease workflow definition: add a transition for one or more
  1064         """method to ease workflow definition: add a transition for one or more
  1063         entity type(s), from one or more state and to a single state
  1065         entity type(s), from one or more state and to a single state
  1064         """
  1066         """
  1067                                conditions, **kwargs)
  1069                                conditions, **kwargs)
  1068         if commit:
  1070         if commit:
  1069             self.commit()
  1071             self.commit()
  1070         return tr.eid
  1072         return tr.eid
  1071 
  1073 
  1072     @deprecated('[3.5] use Transition.set_transition_permissions method')
  1074     @deprecated('[3.5] use Transition.set_transition_permissions method',
       
  1075                 stacklevel=3)
  1073     def cmd_set_transition_permissions(self, treid,
  1076     def cmd_set_transition_permissions(self, treid,
  1074                                        requiredgroups=(), conditions=(),
  1077                                        requiredgroups=(), conditions=(),
  1075                                        reset=True, commit=False):
  1078                                        reset=True, commit=False):
  1076         """set or add (if `reset` is False) groups and conditions for a
  1079         """set or add (if `reset` is False) groups and conditions for a
  1077         transition
  1080         transition
  1079         tr = self._cw.entity_from_eid(treid)
  1082         tr = self._cw.entity_from_eid(treid)
  1080         tr.set_transition_permissions(requiredgroups, conditions, reset)
  1083         tr.set_transition_permissions(requiredgroups, conditions, reset)
  1081         if commit:
  1084         if commit:
  1082             self.commit()
  1085             self.commit()
  1083 
  1086 
  1084     @deprecated('[3.5] use entity.fire_transition("transition") or entity.change_state("state")')
  1087     @deprecated('[3.5] use entity.fire_transition("transition") or entity.change_state("state")',
       
  1088                 stacklevel=3)
  1085     def cmd_set_state(self, eid, statename, commit=False):
  1089     def cmd_set_state(self, eid, statename, commit=False):
  1086         self._cw.entity_from_eid(eid).change_state(statename)
  1090         self._cw.entity_from_eid(eid).change_state(statename)
  1087         if commit:
  1091         if commit:
  1088             self.commit()
  1092             self.commit()
  1089 
  1093 
  1121         entity = self._cw.create_entity(etype, **kwargs)
  1125         entity = self._cw.create_entity(etype, **kwargs)
  1122         if commit:
  1126         if commit:
  1123             self.commit()
  1127             self.commit()
  1124         return entity
  1128         return entity
  1125 
  1129 
  1126     @deprecated('use create_entity')
  1130     @deprecated('[3.5] use create_entity', stacklevel=3)
  1127     def cmd_add_entity(self, etype, *args, **kwargs):
  1131     def cmd_add_entity(self, etype, *args, **kwargs):
  1128         """add a new entity of the given type"""
  1132         """add a new entity of the given type"""
  1129         return self.cmd_create_entity(etype, *args, **kwargs).eid
  1133         return self.cmd_create_entity(etype, *args, **kwargs).eid
  1130 
  1134 
  1131     def sqlexec(self, sql, args=None, ask_confirm=True):
  1135     def sqlexec(self, sql, args=None, ask_confirm=True):
  1132         """execute the given sql if confirmed
  1136         """execute the given sql if confirmed
  1133 
  1137 
  1134         should only be used for low level stuff undoable with existing higher
  1138         should only be used for low level stuff undoable with existing higher
  1135         level actions
  1139         level actions
  1136         """
  1140         """
  1137         if not ask_confirm or self.confirm('execute sql: %s ?' % sql):
  1141         if not ask_confirm or self.confirm('Execute sql: %s ?' % sql):
  1138             self.session.set_pool() # ensure pool is set
  1142             self.session.set_pool() # ensure pool is set
  1139             try:
  1143             try:
  1140                 cu = self.session.system_sql(sql, args)
  1144                 cu = self.session.system_sql(sql, args)
  1141             except:
  1145             except:
  1142                 ex = sys.exc_info()[1]
  1146                 ex = sys.exc_info()[1]
  1143                 if self.confirm('error: %s\nabort?' % ex):
  1147                 if self.confirm('Error: %s\nabort?' % ex):
  1144                     raise
  1148                     raise
  1145                 return
  1149                 return
  1146             try:
  1150             try:
  1147                 return cu.fetchall()
  1151                 return cu.fetchall()
  1148             except:
  1152             except:
  1152     def rqlexec(self, rql, kwargs=None, cachekey=None, ask_confirm=True):
  1156     def rqlexec(self, rql, kwargs=None, cachekey=None, ask_confirm=True):
  1153         """rql action"""
  1157         """rql action"""
  1154         if not isinstance(rql, (tuple, list)):
  1158         if not isinstance(rql, (tuple, list)):
  1155             rql = ( (rql, kwargs), )
  1159             rql = ( (rql, kwargs), )
  1156         res = None
  1160         res = None
       
  1161         try:
       
  1162             execute = self._cw.unsafe_execute
       
  1163         except AttributeError:
       
  1164             execute = self._cw.execute
  1157         for rql, kwargs in rql:
  1165         for rql, kwargs in rql:
  1158             if kwargs:
  1166             if kwargs:
  1159                 msg = '%s (%s)' % (rql, kwargs)
  1167                 msg = '%s (%s)' % (rql, kwargs)
  1160             else:
  1168             else:
  1161                 msg = rql
  1169                 msg = rql
  1162             if not ask_confirm or self.confirm('execute rql: %s ?' % msg):
  1170             if not ask_confirm or self.confirm('Execute rql: %s ?' % msg):
  1163                 try:
  1171                 try:
  1164                     res = self._cw.execute(rql, kwargs, cachekey)
  1172                     res = execute(rql, kwargs, cachekey)
  1165                 except Exception, ex:
  1173                 except Exception, ex:
  1166                     if self.confirm('error: %s\nabort?' % ex):
  1174                     if self.confirm('Error: %s\nabort?' % ex):
  1167                         raise
  1175                         raise
  1168         return res
  1176         return res
  1169 
  1177 
  1170     def rqliter(self, rql, kwargs=None, ask_confirm=True):
  1178     def rqliter(self, rql, kwargs=None, ask_confirm=True):
  1171         return ForRqlIterator(self, rql, None, ask_confirm)
  1179         return ForRqlIterator(self, rql, None, ask_confirm)
  1172 
  1180 
  1173     def cmd_deactivate_verification_hooks(self):
  1181     def cmd_deactivate_verification_hooks(self):
  1174         self.repo.hm.deactivate_verification_hooks()
  1182         self.config.disabled_hooks_categories.add('integrity')
  1175 
  1183 
  1176     def cmd_reactivate_verification_hooks(self):
  1184     def cmd_reactivate_verification_hooks(self):
  1177         self.repo.hm.reactivate_verification_hooks()
  1185         self.config.disabled_hooks_categories.remove('integrity')
  1178 
  1186 
  1179     # broken db commands ######################################################
  1187     # broken db commands ######################################################
  1180 
  1188 
  1181     def cmd_change_attribute_type(self, etype, attr, newtype, commit=True):
  1189     def cmd_change_attribute_type(self, etype, attr, newtype, commit=True):
  1182         """low level method to change the type of an entity attribute. This is
  1190         """low level method to change the type of an entity attribute. This is
  1246         if kwargs:
  1254         if kwargs:
  1247             msg = '%s (%s)' % (rql, kwargs)
  1255             msg = '%s (%s)' % (rql, kwargs)
  1248         else:
  1256         else:
  1249             msg = rql
  1257             msg = rql
  1250         if self.ask_confirm:
  1258         if self.ask_confirm:
  1251             if not self._h.confirm('execute rql: %s ?' % msg):
  1259             if not self._h.confirm('Execute rql: %s ?' % msg):
  1252                 raise StopIteration
  1260                 raise StopIteration
  1253         try:
  1261         try:
  1254             rset = self._h._cw.execute(rql, kwargs)
  1262             rset = self._h._cw.execute(rql, kwargs)
  1255         except Exception, ex:
  1263         except Exception, ex:
  1256             if self._h.confirm('error: %s\nabort?' % ex):
  1264             if self._h.confirm('Error: %s\nabort?' % ex):
  1257                 raise
  1265                 raise
  1258             else:
  1266             else:
  1259                 raise StopIteration
  1267                 raise StopIteration
  1260         self._rsetit = iter(rset)
  1268         self._rsetit = iter(rset)
  1261         return self._rsetit.next()
  1269         return self._rsetit.next()