diff -r 5fe84a5f7035 -r 3f408c7a164e server/migractions.py --- 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 ######################################################