server/migractions.py
branchtls-sprint
changeset 1399 3f408c7a164e
parent 1398 5fe84a5f7035
child 1409 f4dee84a618f
--- a/server/migractions.py	Fri Apr 17 16:55:37 2009 +0200
+++ b/server/migractions.py	Fri Apr 17 17:55:50 2009 +0200
@@ -21,6 +21,7 @@
 from os.path import join, exists
 from datetime import datetime
 
+from logilab.common.deprecation import deprecated_function, obsolete
 from logilab.common.decorators import cached
 from logilab.common.adbh import get_adv_func_helper
 
@@ -28,6 +29,7 @@
 from yams.schema2sql import eschema2sql, rschema2sql
 
 from cubicweb import AuthenticationError, ETYPE_NAME_MAP
+from cubicweb.schema import CubicWebRelationSchema
 from cubicweb.dbapi import get_repository, repo_connect
 from cubicweb.common.migration import MigrationHelper, yes
 
@@ -223,12 +225,12 @@
                         'rql': self.rqlexec,
                         'rqliter': self.rqliter,
                         'schema': self.repo.schema,
-                        # XXX deprecate
-                        'newschema': self.fs_schema,
                         'fsschema': self.fs_schema,
-                        'cnx': self.cnx,
                         'session' : self.session,
                         'repo' : self.repo,
+                        'synchronize_schema': deprecated_function(self.sync_schema_props_perms),
+                        'synchronize_eschema': deprecated_function(self.sync_schema_props_perms),
+                        'synchronize_rschema': deprecated_function(self.sync_schema_props_perms),
                         })
         return context
 
@@ -264,6 +266,200 @@
                                                'after_add_entity', '')
                     self.reactivate_verification_hooks()
     
+    # schema synchronization internals ########################################
+    
+    def _synchronize_permissions(self, ertype):
+        """permission synchronization for an entity or relation type"""
+        if ertype in ('eid', 'has_text', 'identity'):
+            return
+        newrschema = self.fs_schema[ertype]
+        teid = self.repo.schema[ertype].eid
+        if 'update' in newrschema.ACTIONS or newrschema.is_final():
+            # entity type
+            exprtype = u'ERQLExpression'
+        else:
+            # relation type
+            exprtype = u'RRQLExpression'
+        assert teid, ertype
+        gm = self.group_mapping()
+        confirm = self.verbosity >= 2
+        # * remove possibly deprecated permission (eg in the persistent schema
+        #   but not in the new schema)
+        # * synchronize existing expressions
+        # * add new groups/expressions
+        for action in newrschema.ACTIONS:
+            perm = '%s_permission' % action
+            # handle groups
+            newgroups = list(newrschema.get_groups(action))
+            for geid, gname in self.rqlexec('Any G, GN WHERE T %s G, G name GN, '
+                                            'T eid %%(x)s' % perm, {'x': teid}, 'x',
+                                            ask_confirm=False):
+                if not gname in newgroups:
+                    if not confirm or self.confirm('remove %s permission of %s to %s?'
+                                                   % (action, ertype, gname)):
+                        self.rqlexec('DELETE T %s G WHERE G eid %%(x)s, T eid %s'
+                                     % (perm, teid),
+                                     {'x': geid}, 'x', ask_confirm=False)
+                else:
+                    newgroups.remove(gname)
+            for gname in newgroups:
+                if not confirm or self.confirm('grant %s permission of %s to %s?'
+                                               % (action, ertype, gname)):
+                    self.rqlexec('SET T %s G WHERE G eid %%(x)s, T eid %s'
+                                 % (perm, teid),
+                                 {'x': gm[gname]}, 'x', ask_confirm=False)
+            # handle rql expressions
+            newexprs = dict((expr.expression, expr) for expr in newrschema.get_rqlexprs(action))
+            for expreid, expression in self.rqlexec('Any E, EX WHERE T %s E, E expression EX, '
+                                                    'T eid %s' % (perm, teid),
+                                                    ask_confirm=False):
+                if not expression in newexprs:
+                    if not confirm or self.confirm('remove %s expression for %s permission of %s?'
+                                                   % (expression, action, ertype)):
+                        # deleting the relation will delete the expression entity
+                        self.rqlexec('DELETE T %s E WHERE E eid %%(x)s, T eid %s'
+                                     % (perm, teid),
+                                     {'x': expreid}, 'x', ask_confirm=False)
+                else:
+                    newexprs.pop(expression)
+            for expression in newexprs.values():
+                expr = expression.expression
+                if not confirm or self.confirm('add %s expression for %s permission of %s?'
+                                               % (expr, action, ertype)):
+                    self.rqlexec('INSERT RQLExpression X: X exprtype %%(exprtype)s, '
+                                 'X expression %%(expr)s, X mainvars %%(vars)s, T %s X '
+                                 'WHERE T eid %%(x)s' % perm,
+                                 {'expr': expr, 'exprtype': exprtype,
+                                  'vars': expression.mainvars, 'x': teid}, 'x',
+                                 ask_confirm=False)
+        
+    def _synchronize_rschema(self, rtype, syncrdefs=True, syncperms=True):
+        """synchronize properties of the persistent relation schema against its
+        current definition:
+        
+        * description
+        * symetric, meta
+        * inlined
+        * relation definitions if `syncrdefs`
+        * permissions if `syncperms`
+        
+        physical schema changes should be handled by repository's schema hooks
+        """
+        rtype = str(rtype)
+        if rtype in self._synchronized:
+            return
+        self._synchronized.add(rtype)
+        rschema = self.fs_schema.rschema(rtype)
+        self.rqlexecall(ss.updaterschema2rql(rschema),
+                        ask_confirm=self.verbosity>=2)
+        reporschema = self.repo.schema.rschema(rtype)
+        if syncrdefs:
+            for subj, obj in rschema.iter_rdefs():
+                if not reporschema.has_rdef(subj, obj):
+                    continue
+                self._synchronize_rdef_schema(subj, rschema, obj)
+        if syncperms:
+            self._synchronize_permissions(rtype)
+                
+    def _synchronize_eschema(self, etype, syncperms=True):
+        """synchronize properties of the persistent entity schema against
+        its current definition:
+        
+        * description
+        * internationalizable, fulltextindexed, indexed, meta
+        * relations from/to this entity
+        * permissions if `syncperms`
+        """
+        etype = str(etype)
+        if etype in self._synchronized:
+            return
+        self._synchronized.add(etype)
+        repoeschema = self.repo.schema.eschema(etype)
+        try:
+            eschema = self.fs_schema.eschema(etype)
+        except KeyError:
+            return
+        repospschema = repoeschema.specializes()
+        espschema = eschema.specializes()
+        if repospschema and not espschema:
+            self.rqlexec('DELETE X specializes Y WHERE X is CWEType, X name %(x)s',
+                         {'x': str(repoeschema)})
+        elif not repospschema and espschema:
+            self.rqlexec('SET X specializes Y WHERE X is CWEType, X name %(x)s, '
+                         'Y is CWEType, Y name %(y)s',
+                         {'x': str(repoeschema), 'y': str(espschema)})
+        self.rqlexecall(ss.updateeschema2rql(eschema),
+                        ask_confirm=self.verbosity >= 2)
+        for rschema, targettypes, x in eschema.relation_definitions(True):
+            if x == 'subject':
+                if not rschema in repoeschema.subject_relations():
+                    continue
+                subjtypes, objtypes = [etype], targettypes
+            else: # x == 'object'
+                if not rschema in repoeschema.object_relations():
+                    continue
+                subjtypes, objtypes = targettypes, [etype]
+            self._synchronize_rschema(rschema, syncperms=syncperms,
+                                      syncrdefs=False)
+            reporschema = self.repo.schema.rschema(rschema)
+            for subj in subjtypes:
+                for obj in objtypes:
+                    if not reporschema.has_rdef(subj, obj):
+                        continue
+                    self._synchronize_rdef_schema(subj, rschema, obj,
+                                                     commit=False)
+        if syncperms:
+            self._synchronize_permissions(etype)
+
+    def _synchronize_rdef_schema(self, subjtype, rtype, objtype):
+        """synchronize properties of the persistent relation definition schema
+        against its current definition:
+        * order and other properties
+        * constraints
+        """
+        subjtype, objtype = str(subjtype), str(objtype)
+        rschema = self.fs_schema.rschema(rtype)
+        reporschema = self.repo.schema.rschema(rschema)
+        if (subjtype, rschema, objtype) in self._synchronized:
+            return
+        self._synchronized.add((subjtype, rschema, objtype))
+        if rschema.symetric:
+            self._synchronized.add((objtype, rschema, subjtype))
+        confirm = self.verbosity >= 2
+        # properties
+        self.rqlexecall(ss.updaterdef2rql(rschema, subjtype, objtype),
+                        ask_confirm=confirm)
+        # constraints
+        newconstraints = list(rschema.rproperty(subjtype, objtype, 'constraints'))
+        # 1. remove old constraints and update constraints of the same type
+        # NOTE: don't use rschema.constraint_by_type because it may be
+        #       out of sync with newconstraints when multiple
+        #       constraints of the same type are used
+        for cstr in reporschema.rproperty(subjtype, objtype, 'constraints'):
+            for newcstr in newconstraints:
+                if newcstr.type() == cstr.type():
+                    break
+            else:
+                newcstr = None
+            if newcstr is None:
+                self.rqlexec('DELETE X constrained_by C WHERE C eid %(x)s',
+                             {'x': cstr.eid}, 'x',
+                             ask_confirm=confirm)
+                self.rqlexec('DELETE CWConstraint C WHERE C eid %(x)s',
+                             {'x': cstr.eid}, 'x',
+                             ask_confirm=confirm)
+            else:
+                newconstraints.remove(newcstr)
+                values = {'x': cstr.eid,
+                          'v': unicode(newcstr.serialize())}
+                self.rqlexec('SET X value %(v)s WHERE X eid %(x)s',
+                             values, 'x', ask_confirm=confirm)
+        # 2. add new constraints
+        for newcstr in newconstraints:
+            self.rqlexecall(ss.constraint2rql(rschema, subjtype, objtype,
+                                              newcstr),
+                            ask_confirm=confirm)
+            
     # base actions ############################################################
 
     def checkpoint(self):
@@ -568,224 +764,44 @@
         if commit:
             self.commit()
         
-    def cmd_synchronize_permissions(self, ertype, commit=True):
-        """permission synchronization for an entity or relation type"""
-        if ertype in ('eid', 'has_text', 'identity'):
-            return
-        newrschema = self.fs_schema[ertype]
-        teid = self.repo.schema[ertype].eid
-        if 'update' in newrschema.ACTIONS or newrschema.is_final():
-            # entity type
-            exprtype = u'ERQLExpression'
-        else:
-            # relation type
-            exprtype = u'RRQLExpression'
-        assert teid, ertype
-        gm = self.group_mapping()
-        confirm = self.verbosity >= 2
-        # * remove possibly deprecated permission (eg in the persistent schema
-        #   but not in the new schema)
-        # * synchronize existing expressions
-        # * add new groups/expressions
-        for action in newrschema.ACTIONS:
-            perm = '%s_permission' % action
-            # handle groups
-            newgroups = list(newrschema.get_groups(action))
-            for geid, gname in self.rqlexec('Any G, GN WHERE T %s G, G name GN, '
-                                            'T eid %%(x)s' % perm, {'x': teid}, 'x',
-                                            ask_confirm=False):
-                if not gname in newgroups:
-                    if not confirm or self.confirm('remove %s permission of %s to %s?'
-                                                   % (action, ertype, gname)):
-                        self.rqlexec('DELETE T %s G WHERE G eid %%(x)s, T eid %s'
-                                     % (perm, teid),
-                                     {'x': geid}, 'x', ask_confirm=False)
-                else:
-                    newgroups.remove(gname)
-            for gname in newgroups:
-                if not confirm or self.confirm('grant %s permission of %s to %s?'
-                                               % (action, ertype, gname)):
-                    self.rqlexec('SET T %s G WHERE G eid %%(x)s, T eid %s'
-                                 % (perm, teid),
-                                 {'x': gm[gname]}, 'x', ask_confirm=False)
-            # handle rql expressions
-            newexprs = dict((expr.expression, expr) for expr in newrschema.get_rqlexprs(action))
-            for expreid, expression in self.rqlexec('Any E, EX WHERE T %s E, E expression EX, '
-                                                    'T eid %s' % (perm, teid),
-                                                    ask_confirm=False):
-                if not expression in newexprs:
-                    if not confirm or self.confirm('remove %s expression for %s permission of %s?'
-                                                   % (expression, action, ertype)):
-                        # deleting the relation will delete the expression entity
-                        self.rqlexec('DELETE T %s E WHERE E eid %%(x)s, T eid %s'
-                                     % (perm, teid),
-                                     {'x': expreid}, 'x', ask_confirm=False)
-                else:
-                    newexprs.pop(expression)
-            for expression in newexprs.values():
-                expr = expression.expression
-                if not confirm or self.confirm('add %s expression for %s permission of %s?'
-                                               % (expr, action, ertype)):
-                    self.rqlexec('INSERT RQLExpression X: X exprtype %%(exprtype)s, '
-                                 'X expression %%(expr)s, X mainvars %%(vars)s, T %s X '
-                                 'WHERE T eid %%(x)s' % perm,
-                                 {'expr': expr, 'exprtype': exprtype,
-                                  'vars': expression.mainvars, 'x': teid}, 'x',
-                                 ask_confirm=False)
-        if commit:
-            self.commit()
-        
-    def cmd_synchronize_rschema(self, rtype, syncrdefs=True, syncperms=True,
-                                commit=True):
-        """synchronize properties of the persistent relation schema against its
-        current definition:
-        
-        * description
-        * symetric, meta
-        * inlined
-        * relation definitions if `syncrdefs`
-        * permissions if `syncperms`
-        
-        physical schema changes should be handled by repository's schema hooks
-        """
-        rtype = str(rtype)
-        if rtype in self._synchronized:
-            return
-        self._synchronized.add(rtype)
-        rschema = self.fs_schema.rschema(rtype)
-        self.rqlexecall(ss.updaterschema2rql(rschema),
-                        ask_confirm=self.verbosity>=2)
-        reporschema = self.repo.schema.rschema(rtype)
-        if syncrdefs:
-            for subj, obj in rschema.iter_rdefs():
-                if not reporschema.has_rdef(subj, obj):
-                    continue
-                self.cmd_synchronize_rdef_schema(subj, rschema, obj,
-                                                 commit=False)
-        if syncperms:
-            self.cmd_synchronize_permissions(rtype, commit=False)
-        if commit:
-            self.commit()
-                
-    def cmd_synchronize_eschema(self, etype, syncperms=True, commit=True):
-        """synchronize properties of the persistent entity schema against
-        its current definition:
-        
-        * description
-        * internationalizable, fulltextindexed, indexed, meta
-        * relations from/to this entity
-        * permissions if `syncperms`
-        """
-        etype = str(etype)
-        if etype in self._synchronized:
-            return
-        self._synchronized.add(etype)
-        repoeschema = self.repo.schema.eschema(etype)
-        try:
-            eschema = self.fs_schema.eschema(etype)
-        except KeyError:
-            return
-        repospschema = repoeschema.specializes()
-        espschema = eschema.specializes()
-        if repospschema and not espschema:
-            self.rqlexec('DELETE X specializes Y WHERE X is CWEType, X name %(x)s',
-                         {'x': str(repoeschema)})
-        elif not repospschema and espschema:
-            self.rqlexec('SET X specializes Y WHERE X is CWEType, X name %(x)s, '
-                         'Y is CWEType, Y name %(y)s',
-                         {'x': str(repoeschema), 'y': str(espschema)})
-        self.rqlexecall(ss.updateeschema2rql(eschema),
-                        ask_confirm=self.verbosity >= 2)
-        for rschema, targettypes, x in eschema.relation_definitions(True):
-            if x == 'subject':
-                if not rschema in repoeschema.subject_relations():
-                    continue
-                subjtypes, objtypes = [etype], targettypes
-            else: # x == 'object'
-                if not rschema in repoeschema.object_relations():
-                    continue
-                subjtypes, objtypes = targettypes, [etype]
-            self.cmd_synchronize_rschema(rschema, syncperms=syncperms,
-                                         syncrdefs=False, commit=False)
-            reporschema = self.repo.schema.rschema(rschema)
-            for subj in subjtypes:
-                for obj in objtypes:
-                    if not reporschema.has_rdef(subj, obj):
-                        continue
-                    self.cmd_synchronize_rdef_schema(subj, rschema, obj,
-                                                     commit=False)
-        if syncperms:
-            self.cmd_synchronize_permissions(etype, commit=False)
-        if commit:
-            self.commit()
-
-    def cmd_synchronize_rdef_schema(self, subjtype, rtype, objtype,
-                                    commit=True):
-        """synchronize properties of the persistent relation definition schema
-        against its current definition:
-        * order and other properties
-        * constraints
-        """
-        subjtype, objtype = str(subjtype), str(objtype)
-        rschema = self.fs_schema.rschema(rtype)
-        reporschema = self.repo.schema.rschema(rschema)
-        if (subjtype, rschema, objtype) in self._synchronized:
-            return
-        self._synchronized.add((subjtype, rschema, objtype))
-        if rschema.symetric:
-            self._synchronized.add((objtype, rschema, subjtype))
-        confirm = self.verbosity >= 2
-        # properties
-        self.rqlexecall(ss.updaterdef2rql(rschema, subjtype, objtype),
-                        ask_confirm=confirm)
-        # constraints
-        newconstraints = list(rschema.rproperty(subjtype, objtype, 'constraints'))
-        # 1. remove old constraints and update constraints of the same type
-        # NOTE: don't use rschema.constraint_by_type because it may be
-        #       out of sync with newconstraints when multiple
-        #       constraints of the same type are used
-        for cstr in reporschema.rproperty(subjtype, objtype, 'constraints'):
-            for newcstr in newconstraints:
-                if newcstr.type() == cstr.type():
-                    break
-            else:
-                newcstr = None
-            if newcstr is None:
-                self.rqlexec('DELETE X constrained_by C WHERE C eid %(x)s',
-                             {'x': cstr.eid}, 'x',
-                             ask_confirm=confirm)
-                self.rqlexec('DELETE CWConstraint C WHERE C eid %(x)s',
-                             {'x': cstr.eid}, 'x',
-                             ask_confirm=confirm)
-            else:
-                newconstraints.remove(newcstr)
-                values = {'x': cstr.eid,
-                          'v': unicode(newcstr.serialize())}
-                self.rqlexec('SET X value %(v)s WHERE X eid %(x)s',
-                             values, 'x', ask_confirm=confirm)
-        # 2. add new constraints
-        for newcstr in newconstraints:
-            self.rqlexecall(ss.constraint2rql(rschema, subjtype, objtype,
-                                              newcstr),
-                            ask_confirm=confirm)
-        if commit:
-            self.commit()
-        
-    def cmd_synchronize_schema(self, syncperms=True, commit=True):
+    def cmd_sync_schema_props_perms(self, ertype=None, syncperms=True,
+                                    syncprops=True, syncrdefs=True, commit=True)
         """synchronize the persistent schema against the current definition
         schema.
         
         It will synch common stuff between the definition schema and the
         actual persistent schema, it won't add/remove any entity or relation.
         """
-        for etype in self.repo.schema.entities():
-            self.cmd_synchronize_eschema(etype, syncperms=syncperms, commit=False)
+        assert syncperms or syncprops, 'nothing to do'
+        if ertype is not None:
+            if isinstance(ertype, (tuple, list)):
+                assert len(ertype) == 3, 'not a relation definition'
+                assert syncprops, 'can\'t update permission for a relation definition'
+                self._synchronize_rdef_schema(*ertype)
+            elif syncprops:
+                erschema = self.repo.schema[ertype]
+                if isinstance(erschema, CubicWebRelationSchema):
+                    self._synchronize_rschema(erschema, syncperms=syncperms,
+                                              syncrdefs=syncrdefs)
+                else:
+                    self._synchronize_eschema(erschema, syncperms=syncperms)
+            else:
+                self._synchronize_permissions(ertype)
+        else:
+            for etype in self.repo.schema.entities():
+                if syncprops:
+                    self._synchronize_eschema(etype, syncperms=syncperms)
+                else:
+                    self._synchronize_permissions(etype)
         if commit:
             self.commit()
                 
     def cmd_change_relation_props(self, subjtype, rtype, objtype,
                                   commit=True, **kwargs):
-        """change some properties of a relation definition"""
+        """change some properties of a relation definition
+
+        you usually want to use sync_schema_props_perms instead.
+        """
         assert kwargs
         restriction = []
         if subjtype and subjtype != 'Any':
@@ -808,7 +824,9 @@
     def cmd_set_size_constraint(self, etype, rtype, size, commit=True):
         """set change size constraint of a string attribute
 
-        if size is None any size constraint will be removed
+        if size is None any size constraint will be removed.
+        
+        you usually want to use sync_schema_props_perms instead.        
         """
         oldvalue = None
         for constr in self.repo.schema.eschema(etype).constraints(rtype):
@@ -840,6 +858,10 @@
                 self.rqlexec('DELETE CWConstraint C WHERE NOT X constrained_by C')
         if commit:
             self.commit()
+
+    @obsolete('use sync_schema_props_perms(ertype, syncprops=False)')
+    def cmd_synchronize_permissions(self, ertype, commit=True):
+        self.cmd_sync_schema_props_perms(ertype, syncprops=False, commit=commit)
     
     # Workflows handling ######################################################