diff -r 058bb3dc685f -r 0b59724cb3f2 server/schemaserial.py --- a/server/schemaserial.py Mon Jan 04 18:40:30 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,656 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""functions for schema / permissions (de)serialization using RQL""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import os -import json -import sys - -from six import PY2, text_type, string_types - -from logilab.common.shellutils import ProgressBar, DummyProgressBar - -from yams import BadSchemaDefinition, schema as schemamod, buildobjs as ybo - -from cubicweb import Binary -from cubicweb.schema import (KNOWN_RPROPERTIES, CONSTRAINTS, ETYPE_NAME_MAP, - VIRTUAL_RTYPES) -from cubicweb.server import sqlutils, schema2sql as y2sql - - -def group_mapping(cnx, interactive=True): - """create a group mapping from an rql cursor - - A group mapping has standard group names as key (managers, owners at least) - and the actual CWGroup entity's eid as associated value. - In interactive mode (the default), missing groups'eid will be prompted - from the user. - """ - res = {} - for eid, name in cnx.execute('Any G, N WHERE G is CWGroup, G name N', - build_descr=False): - res[name] = eid - if not interactive: - return res - missing = [g for g in ('owners', 'managers', 'users', 'guests') if not g in res] - if missing: - print('some native groups are missing but the following groups have been found:') - print('\n'.join('* %s (%s)' % (n, eid) for n, eid in res.items())) - print() - print('enter the eid of a to group to map to each missing native group') - print('or just type enter to skip permissions granted to a group') - for group in missing: - while True: - value = raw_input('eid for group %s: ' % group).strip() - if not value: - continue - try: - eid = int(value) - except ValueError: - print('eid should be an integer') - continue - for eid_ in res.values(): - if eid == eid_: - break - else: - print('eid is not a group eid') - continue - res[name] = eid - break - return res - -def cstrtype_mapping(cnx): - """cached constraint types mapping""" - map = dict(cnx.execute('Any T, X WHERE X is CWConstraintType, X name T')) - return map - -# schema / perms deserialization ############################################## - -def deserialize_schema(schema, cnx): - """return a schema according to information stored in an rql database - as CWRType and CWEType entities - """ - repo = cnx.repo - dbhelper = repo.system_source.dbhelper - - # Computed Rtype - with cnx.ensure_cnx_set: - tables = set(t.lower() for t in dbhelper.list_tables(cnx.cnxset.cu)) - has_computed_relations = 'cw_cwcomputedrtype' in tables - # computed attribute - try: - cnx.system_sql("SELECT cw_formula FROM cw_CWAttribute") - has_computed_attributes = True - except Exception: - cnx.rollback() - has_computed_attributes = False - - # XXX bw compat (3.6 migration) - sqlcu = cnx.system_sql("SELECT * FROM cw_CWRType WHERE cw_name='symetric'") - if sqlcu.fetchall(): - sql = dbhelper.sql_rename_col('cw_CWRType', 'cw_symetric', 'cw_symmetric', - dbhelper.TYPE_MAPPING['Boolean'], True) - sqlcu.execute(sql) - sqlcu.execute("UPDATE cw_CWRType SET cw_name='symmetric' WHERE cw_name='symetric'") - cnx.commit() - ertidx = {} - copiedeids = set() - permsidx = deserialize_ertype_permissions(cnx) - schema.reading_from_database = True - # load every entity types - for eid, etype, desc in cnx.execute( - 'Any X, N, D WHERE X is CWEType, X name N, X description D', - build_descr=False): - # base types are already in the schema, skip them - if etype in schemamod.BASE_TYPES: - # just set the eid - eschema = schema.eschema(etype) - eschema.eid = eid - ertidx[eid] = etype - continue - if etype in ETYPE_NAME_MAP: - needcopy = False - netype = ETYPE_NAME_MAP[etype] - # can't use write rql queries at this point, use raw sql - sqlexec = cnx.system_sql - if sqlexec('SELECT 1 FROM %(p)sCWEType WHERE %(p)sname=%%(n)s' - % {'p': sqlutils.SQL_PREFIX}, {'n': netype}).fetchone(): - # the new type already exists, we should copy (eg make existing - # instances of the old type instances of the new type) - assert etype.lower() != netype.lower() - needcopy = True - else: - # the new type doesn't exist, we should rename - sqlexec('UPDATE %(p)sCWEType SET %(p)sname=%%(n)s WHERE %(p)seid=%%(x)s' - % {'p': sqlutils.SQL_PREFIX}, {'x': eid, 'n': netype}) - if etype.lower() != netype.lower(): - alter_table_sql = dbhelper.sql_rename_table(sqlutils.SQL_PREFIX+etype, - sqlutils.SQL_PREFIX+netype) - sqlexec(alter_table_sql) - sqlexec('UPDATE entities SET type=%(n)s WHERE type=%(x)s', - {'x': etype, 'n': netype}) - cnx.commit(False) - tocleanup = [eid] - tocleanup += (eid for eid, cached in repo._type_source_cache.items() - if etype == cached[0]) - repo.clear_caches(tocleanup) - cnx.commit(False) - if needcopy: - ertidx[eid] = netype - copiedeids.add(eid) - # copy / CWEType entity removal expected to be done through - # rename_entity_type in a migration script - continue - etype = netype - ertidx[eid] = etype - eschema = schema.add_entity_type( - ybo.EntityType(name=etype, description=desc, eid=eid)) - set_perms(eschema, permsidx) - # load inheritance relations - for etype, stype in cnx.execute( - 'Any XN, ETN WHERE X is CWEType, X name XN, X specializes ET, ET name ETN', - build_descr=False): - etype = ETYPE_NAME_MAP.get(etype, etype) - stype = ETYPE_NAME_MAP.get(stype, stype) - schema.eschema(etype)._specialized_type = stype - schema.eschema(stype)._specialized_by.append(etype) - if has_computed_relations: - rset = cnx.execute( - 'Any X, N, R, D WHERE X is CWComputedRType, X name N, ' - 'X rule R, X description D') - for eid, rule_name, rule, description in rset.rows: - rtype = ybo.ComputedRelation(name=rule_name, rule=rule, eid=eid, - description=description) - rschema = schema.add_relation_type(rtype) - set_perms(rschema, permsidx) - # load every relation types - for eid, rtype, desc, sym, il, ftc in cnx.execute( - 'Any X,N,D,S,I,FTC WHERE X is CWRType, X name N, X description D, ' - 'X symmetric S, X inlined I, X fulltext_container FTC', build_descr=False): - ertidx[eid] = rtype - rschema = schema.add_relation_type( - ybo.RelationType(name=rtype, description=desc, - symmetric=bool(sym), inlined=bool(il), - fulltext_container=ftc, eid=eid)) - # remains to load every relation definitions (ie relations and attributes) - cstrsidx = deserialize_rdef_constraints(cnx) - pendingrdefs = [] - # closure to factorize common code of attribute/relation rdef addition - def _add_rdef(rdefeid, seid, reid, oeid, **kwargs): - rdef = ybo.RelationDefinition(ertidx[seid], ertidx[reid], ertidx[oeid], - constraints=cstrsidx.get(rdefeid, ()), - eid=rdefeid, **kwargs) - if seid in copiedeids or oeid in copiedeids: - # delay addition of this rdef. We'll insert them later if needed. We - # have to do this because: - # - # * on etype renaming, we want relation of the old entity type being - # redirected to the new type during migration - # - # * in the case of a copy, we've to take care that rdef already - # existing in the schema are not overwritten by a redirected one, - # since we want correct eid on them (redirected rdef will be - # removed in rename_entity_type) - pendingrdefs.append(rdef) - else: - # add_relation_def return a RelationDefinitionSchema if it has been - # actually added (can be None on duplicated relation definitions, - # e.g. if the relation type is marked as beeing symmetric) - rdefs = schema.add_relation_def(rdef) - if rdefs is not None: - ertidx[rdefeid] = rdefs - set_perms(rdefs, permsidx) - # Get the type parameters for additional base types. - try: - extra_props = dict(cnx.execute('Any X, XTP WHERE X is CWAttribute, ' - 'X extra_props XTP')) - except Exception: - cnx.critical('Previous CRITICAL notification about extra_props is not ' - 'a problem if you are migrating to cubicweb 3.17') - extra_props = {} # not yet in the schema (introduced by 3.17 migration) - - # load attributes - rql = ('Any X,SE,RT,OE,CARD,ORD,DESC,IDX,FTIDX,I18N,DFLT%(fm)s ' - 'WHERE X is CWAttribute, X relation_type RT, X cardinality CARD,' - ' X ordernum ORD, X indexed IDX, X description DESC, ' - ' X internationalizable I18N, X defaultval DFLT,%(fmsnip)s' - ' X fulltextindexed FTIDX, X from_entity SE, X to_entity OE') - if has_computed_attributes: - rql = rql % {'fm': ',FM', 'fmsnip': 'X formula FM,'} - else: - rql = rql % {'fm': '', 'fmsnip': ''} - for values in cnx.execute(rql, build_descr=False): - attrs = dict(zip( - ('rdefeid', 'seid', 'reid', 'oeid', 'cardinality', - 'order', 'description', 'indexed', 'fulltextindexed', - 'internationalizable', 'default', 'formula'), values)) - typeparams = extra_props.get(attrs['rdefeid']) - attrs.update(json.loads(typeparams.getvalue().decode('ascii')) if typeparams else {}) - default = attrs['default'] - if default is not None: - if isinstance(default, Binary): - # while migrating from 3.17 to 3.18, we still have to - # handle String defaults - attrs['default'] = default.unzpickle() - _add_rdef(**attrs) - # load relations - for values in cnx.execute( - 'Any X,SE,RT,OE,CARD,ORD,DESC,C WHERE X is CWRelation, X relation_type RT,' - 'X cardinality CARD, X ordernum ORD, X description DESC, ' - 'X from_entity SE, X to_entity OE, X composite C', build_descr=False): - rdefeid, seid, reid, oeid, card, ord, desc, comp = values - _add_rdef(rdefeid, seid, reid, oeid, - cardinality=card, description=desc, order=ord, - composite=comp) - for rdef in pendingrdefs: - try: - rdefs = schema.add_relation_def(rdef) - except BadSchemaDefinition: - continue - if rdefs is not None: - set_perms(rdefs, permsidx) - unique_togethers = {} - rset = cnx.execute( - 'Any X,E,R WHERE ' - 'X is CWUniqueTogetherConstraint, ' - 'X constraint_of E, X relations R', build_descr=False) - for values in rset: - uniquecstreid, eeid, releid = values - eschema = schema.schema_by_eid(eeid) - relations = unique_togethers.setdefault(uniquecstreid, (eschema, [])) - rel = ertidx[releid] - if isinstance(rel, schemamod.RelationDefinitionSchema): - # not yet migrated 3.9 database ('relations' target type changed - # to CWRType in 3.10) - rtype = rel.rtype.type - else: - rtype = str(rel) - relations[1].append(rtype) - for eschema, unique_together in unique_togethers.values(): - eschema._unique_together.append(tuple(sorted(unique_together))) - schema.infer_specialization_rules() - cnx.commit() - schema.finalize() - schema.reading_from_database = False - - -def deserialize_ertype_permissions(cnx): - """return sect action:groups associations for the given - entity or relation schema with its eid, according to schema's - permissions stored in the database as [read|add|delete|update]_permission - relations between CWEType/CWRType and CWGroup entities - """ - res = {} - for action in ('read', 'add', 'update', 'delete'): - rql = 'Any E,N WHERE G is CWGroup, G name N, E %s_permission G' % action - for eid, gname in cnx.execute(rql, build_descr=False): - res.setdefault(eid, {}).setdefault(action, []).append(gname) - rql = ('Any E,X,EXPR,V WHERE X is RQLExpression, X expression EXPR, ' - 'E %s_permission X, X mainvars V' % action) - for eid, expreid, expr, mainvars in cnx.execute(rql, build_descr=False): - # we don't know yet if it's a rql expr for an entity or a relation, - # so append a tuple to differentiate from groups and so we'll be - # able to instantiate it later - res.setdefault(eid, {}).setdefault(action, []).append( (expr, mainvars, expreid) ) - return res - -def deserialize_rdef_constraints(cnx): - """return the list of relation definition's constraints as instances""" - res = {} - for rdefeid, ceid, ct, val in cnx.execute( - 'Any E, X,TN,V WHERE E constrained_by X, X is CWConstraint, ' - 'X cstrtype T, T name TN, X value V', build_descr=False): - cstr = CONSTRAINTS[ct].deserialize(val) - cstr.eid = ceid - res.setdefault(rdefeid, []).append(cstr) - return res - -def set_perms(erschema, permsidx): - """set permissions on the given erschema according to the permission - definition dictionary as built by deserialize_ertype_permissions for a - given erschema's eid - """ - # reset erschema permissions here to avoid getting yams default anyway - erschema.permissions = dict((action, ()) for action in erschema.ACTIONS) - try: - thispermsdict = permsidx[erschema.eid] - except KeyError: - return - for action, somethings in thispermsdict.items(): - erschema.permissions[action] = tuple( - isinstance(p, tuple) and erschema.rql_expression(*p) or p - for p in somethings) - - -# schema / perms serialization ################################################ - -def serialize_schema(cnx, schema): - """synchronize schema and permissions in the database according to - current schema - """ - _title = '-> storing the schema in the database ' - print(_title, end=' ') - execute = cnx.execute - eschemas = schema.entities() - pb_size = (len(eschemas + schema.relations()) - + len(CONSTRAINTS) - + len([x for x in eschemas if x.specializes()])) - if sys.stdout.isatty(): - pb = ProgressBar(pb_size, title=_title) - else: - pb = DummyProgressBar() - groupmap = group_mapping(cnx, interactive=False) - # serialize all entity types, assuring CWEType is serialized first for proper - # is / is_instance_of insertion - eschemas.remove(schema.eschema('CWEType')) - eschemas.insert(0, schema.eschema('CWEType')) - for eschema in eschemas: - execschemarql(execute, eschema, eschema2rql(eschema, groupmap)) - pb.update() - # serialize constraint types - cstrtypemap = {} - rql = 'INSERT CWConstraintType X: X name %(ct)s' - for cstrtype in CONSTRAINTS: - cstrtypemap[cstrtype] = execute(rql, {'ct': text_type(cstrtype)}, - build_descr=False)[0][0] - pb.update() - # serialize relations - for rschema in schema.relations(): - # skip virtual relations such as eid, has_text and identity - if rschema in VIRTUAL_RTYPES: - pb.update() - continue - if rschema.rule: - execschemarql(execute, rschema, crschema2rql(rschema, groupmap)) - pb.update() - continue - execschemarql(execute, rschema, rschema2rql(rschema, addrdef=False)) - if rschema.symmetric: - rdefs = [rdef for k, rdef in rschema.rdefs.items() - if (rdef.subject, rdef.object) == k] - else: - rdefs = rschema.rdefs.values() - for rdef in rdefs: - execschemarql(execute, rdef, - rdef2rql(rdef, cstrtypemap, groupmap)) - pb.update() - # serialize unique_together constraints - for eschema in eschemas: - if eschema._unique_together: - execschemarql(execute, eschema, uniquetogether2rqls(eschema)) - # serialize yams inheritance relationships - for rql, kwargs in specialize2rql(schema): - execute(rql, kwargs, build_descr=False) - pb.update() - print() - - -# high level serialization functions - -def execschemarql(execute, schema, rqls): - for rql, kwargs in rqls: - kwargs['x'] = schema.eid - rset = execute(rql, kwargs, build_descr=False) - if schema.eid is None: - schema.eid = rset[0][0] - else: - assert rset - -def erschema2rql(erschema, groupmap): - if isinstance(erschema, schemamod.EntitySchema): - return eschema2rql(erschema, groupmap=groupmap) - return rschema2rql(erschema, groupmap=groupmap) - -def specialize2rql(schema): - for eschema in schema.entities(): - if eschema.final: - continue - for rql, kwargs in eschemaspecialize2rql(eschema): - yield rql, kwargs - -# etype serialization - -def eschema2rql(eschema, groupmap=None): - """return a list of rql insert statements to enter an entity schema - in the database as an CWEType entity - """ - relations, values = eschema_relations_values(eschema) - # NOTE: 'specializes' relation can't be inserted here since there's no - # way to make sure the parent type is inserted before the child type - yield 'INSERT CWEType X: %s' % ','.join(relations) , values - # entity permissions - if groupmap is not None: - for rql, args in _erperms2rql(eschema, groupmap): - yield rql, args - -def eschema_relations_values(eschema): - values = _ervalues(eschema) - relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] - return relations, values - -def eschemaspecialize2rql(eschema): - specialized_type = eschema.specializes() - if specialized_type: - values = {'x': eschema.eid, 'et': specialized_type.eid} - yield 'SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', values - -def uniquetogether2rqls(eschema): - rql_args = [] - # robustness against duplicated CWUniqueTogetherConstraint (pre 3.18) - columnset = set() - for columns in eschema._unique_together: - if columns in columnset: - print('schemaserial: skipping duplicate unique together %r %r' % - (eschema.type, columns)) - continue - columnset.add(columns) - rql, args = _uniquetogether2rql(eschema, columns) - args['name'] = y2sql.unique_index_name(eschema, columns) - rql_args.append((rql, args)) - return rql_args - -def _uniquetogether2rql(eschema, unique_together): - relations = [] - restrictions = [] - substs = {} - for i, name in enumerate(unique_together): - rschema = eschema.schema.rschema(name) - rtype = 'T%d' % i - substs[rtype] = text_type(rschema.type) - relations.append('C relations %s' % rtype) - restrictions.append('%(rtype)s name %%(%(rtype)s)s' % {'rtype': rtype}) - relations = ', '.join(relations) - restrictions = ', '.join(restrictions) - rql = ('INSERT CWUniqueTogetherConstraint C: C name %%(name)s, C constraint_of X, %s ' - 'WHERE X eid %%(x)s, %s') - return rql % (relations, restrictions), substs - - -def _ervalues(erschema): - try: - type_ = text_type(erschema.type) - except UnicodeDecodeError as e: - raise Exception("can't decode %s [was %s]" % (erschema.type, e)) - try: - desc = text_type(erschema.description) or u'' - except UnicodeDecodeError as e: - raise Exception("can't decode %s [was %s]" % (erschema.description, e)) - return { - 'name': type_, - 'final': erschema.final, - 'description': desc, - } - -# rtype serialization - -def rschema2rql(rschema, cstrtypemap=None, addrdef=True, groupmap=None): - """generate rql insert statements to enter a relation schema - in the database as an CWRType entity - """ - if rschema.type == 'has_text': - return - relations, values = rschema_relations_values(rschema) - yield 'INSERT CWRType X: %s' % ','.join(relations), values - if addrdef: - assert cstrtypemap - # sort for testing purpose - for rdef in sorted(rschema.rdefs.values(), - key=lambda x: (x.subject, x.object)): - for rql, values in rdef2rql(rdef, cstrtypemap, groupmap): - yield rql, values - -def rschema_relations_values(rschema): - values = _ervalues(rschema) - values['final'] = rschema.final - values['symmetric'] = rschema.symmetric - values['inlined'] = rschema.inlined - if PY2 and isinstance(rschema.fulltext_container, str): - values['fulltext_container'] = unicode(rschema.fulltext_container) - else: - values['fulltext_container'] = rschema.fulltext_container - relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] - return relations, values - -def crschema2rql(crschema, groupmap): - relations, values = crschema_relations_values(crschema) - yield 'INSERT CWComputedRType X: %s' % ','.join(relations), values - if groupmap: - for rql, args in _erperms2rql(crschema, groupmap): - yield rql, args - -def crschema_relations_values(crschema): - values = _ervalues(crschema) - values['rule'] = text_type(crschema.rule) - # XXX why oh why? - del values['final'] - relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] - return relations, values - -# rdef serialization - -def rdef2rql(rdef, cstrtypemap, groupmap=None): - # don't serialize inferred relations - if rdef.infered: - return - relations, values = _rdef_values(rdef) - relations.append('X relation_type ER,X from_entity SE,X to_entity OE') - values.update({'se': rdef.subject.eid, 'rt': rdef.rtype.eid, 'oe': rdef.object.eid}) - if rdef.final: - etype = 'CWAttribute' - else: - etype = 'CWRelation' - yield 'INSERT %s X: %s WHERE SE eid %%(se)s,ER eid %%(rt)s,OE eid %%(oe)s' % ( - etype, ','.join(relations), ), values - for rql, values in constraints2rql(cstrtypemap, rdef.constraints): - yield rql, values - # no groupmap means "no security insertion" - if groupmap: - for rql, args in _erperms2rql(rdef, groupmap): - yield rql, args - -_IGNORED_PROPS = ['eid', 'constraints', 'uid', 'infered', 'permissions'] - -def _rdef_values(rdef): - amap = {'order': 'ordernum', 'default': 'defaultval'} - values = {} - extra = {} - for prop in rdef.rproperty_defs(rdef.object): - if prop in _IGNORED_PROPS: - continue - value = getattr(rdef, prop) - if prop not in KNOWN_RPROPERTIES: - extra[prop] = value - continue - # XXX type cast really necessary? - if prop in ('indexed', 'fulltextindexed', 'internationalizable'): - value = bool(value) - elif prop == 'ordernum': - value = int(value) - elif PY2 and isinstance(value, str): - value = unicode(value) - if value is not None and prop == 'default': - value = Binary.zpickle(value) - values[amap.get(prop, prop)] = value - if extra: - values['extra_props'] = Binary(json.dumps(extra).encode('ascii')) - relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] - return relations, values - -def constraints2rql(cstrtypemap, constraints, rdefeid=None): - for constraint in constraints: - values = {'ct': cstrtypemap[constraint.type()], - 'value': text_type(constraint.serialize()), - 'x': rdefeid} # when not specified, will have to be set by the caller - yield 'INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE \ -CT eid %(ct)s, EDEF eid %(x)s', values - - -def _erperms2rql(erschema, groupmap): - """return rql insert statements to enter the entity or relation - schema's permissions in the database as - [read|add|delete|update]_permission relations between CWEType/CWRType - and CWGroup entities - """ - for action in erschema.ACTIONS: - try: - grantedto = erschema.action_permissions(action) - except KeyError: - # may occurs when modifying persistent schema - continue - for group_or_rqlexpr in grantedto: - if isinstance(group_or_rqlexpr, string_types): - # group - try: - yield ('SET X %s_permission Y WHERE Y eid %%(g)s, X eid %%(x)s' % action, - {'g': groupmap[group_or_rqlexpr]}) - except KeyError: - print("WARNING: group %s used in permissions for %s was ignored because it doesn't exist." - " You may want to add it into a precreate.py file" % (group_or_rqlexpr, erschema)) - continue - else: - # rqlexpr - rqlexpr = group_or_rqlexpr - yield ('INSERT RQLExpression E: E expression %%(e)s, E exprtype %%(t)s, ' - 'E mainvars %%(v)s, X %s_permission E WHERE X eid %%(x)s' % action, - {'e': text_type(rqlexpr.expression), - 'v': text_type(','.join(sorted(rqlexpr.mainvars))), - 't': text_type(rqlexpr.__class__.__name__)}) - -# update functions - -def updateeschema2rql(eschema, eid): - relations, values = eschema_relations_values(eschema) - values['x'] = eid - yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values - -def updaterschema2rql(rschema, eid): - if rschema.rule: - yield ('SET X rule %(r)s WHERE X eid %(x)s', - {'x': eid, 'r': text_type(rschema.rule)}) - else: - relations, values = rschema_relations_values(rschema) - values['x'] = eid - yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values - -def updaterdef2rql(rdef, eid): - relations, values = _rdef_values(rdef) - values['x'] = eid - yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values