server/schemaserial.py
changeset 4763 81b0df087375
parent 4760 fe0e307b9b70
child 4790 52c81aef0b61
equal deleted inserted replaced
4762:8dce25da9d95 4763:81b0df087375
    47                     res[group] = int(value)
    47                     res[group] = int(value)
    48                 except ValueError:
    48                 except ValueError:
    49                     print 'eid should be an integer'
    49                     print 'eid should be an integer'
    50                     continue
    50                     continue
    51     return res
    51     return res
       
    52 
       
    53 def cstrtype_mapping(cursor):
       
    54     """cached constraint types mapping"""
       
    55     return dict(cursor.execute('Any T, X WHERE X is CWConstraintType, X name T'))
    52 
    56 
    53 # schema / perms deserialization ##############################################
    57 # schema / perms deserialization ##############################################
    54 def deserialize_schema(schema, session):
    58 def deserialize_schema(schema, session):
    55     """return a schema according to information stored in an rql database
    59     """return a schema according to information stored in an rql database
    56     as CWRType and CWEType entities
    60     as CWRType and CWEType entities
   219     if not quiet:
   223     if not quiet:
   220         pb_size = len(aller) + len(CONSTRAINTS) + len([x for x in eschemas if x.specializes()])
   224         pb_size = len(aller) + len(CONSTRAINTS) + len([x for x in eschemas if x.specializes()])
   221         pb = ProgressBar(pb_size, title=_title)
   225         pb = ProgressBar(pb_size, title=_title)
   222     else:
   226     else:
   223         pb = None
   227         pb = None
       
   228     groupmap = group_mapping(cursor, interactive=False)
   224     # serialize all entity types, assuring CWEType is serialized first
   229     # serialize all entity types, assuring CWEType is serialized first
   225     eschemas.remove(schema.eschema('CWEType'))
   230     eschemas.remove(schema.eschema('CWEType'))
   226     eschemas.insert(0, schema.eschema('CWEType'))
   231     eschemas.insert(0, schema.eschema('CWEType'))
   227     for eschema in eschemas:
   232     for eschema in eschemas:
   228         execschemarql(execute, eschema, eschema2rql(eschema, groupmap))
   233         execschemarql(execute, eschema, eschema2rql(eschema, groupmap))
   229         if pb is not None:
   234         if pb is not None:
   230             pb.update()
   235             pb.update()
   231     # serialize constraint types
   236     # serialize constraint types
       
   237     cstrtypemap = {}
   232     rql = 'INSERT CWConstraintType X: X name %(ct)s'
   238     rql = 'INSERT CWConstraintType X: X name %(ct)s'
   233     for cstrtype in CONSTRAINTS:
   239     for cstrtype in CONSTRAINTS:
   234         execute(rql, {'ct': unicode(cstrtype)}, build_descr=False)
   240         cstrtypemap[cstrtype] = execute(rql, {'ct': unicode(cstrtype)},
       
   241                                         build_descr=False)[0][0]
   235         if pb is not None:
   242         if pb is not None:
   236             pb.update()
   243             pb.update()
   237     # serialize relations
   244     # serialize relations
   238     for rschema in schema.relations():
   245     for rschema in schema.relations():
   239         # skip virtual relations such as eid, has_text and identity
   246         # skip virtual relations such as eid, has_text and identity
   240         if rschema in VIRTUAL_RTYPES:
   247         if rschema in VIRTUAL_RTYPES:
   241             if pb is not None:
   248             if pb is not None:
   242                 pb.update()
   249                 pb.update()
   243             continue
   250             continue
   244         for rql, kwargs in erschema2rql(schema[ertype], groupmap):
   251         execschemarql(execute, rschema, rschema2rql(rschema, addrdef=False))
   245             execute(rql, kwargs, build_descr=False)
   252         if rschema.symmetric:
       
   253             rdefs = [rdef for k, rdef in rschema.rdefs.iteritems()
       
   254                      if (rdef.subject, rdef.object) == k]
       
   255         else:
       
   256             rdefs = rschema.rdefs.itervalues()
       
   257         for rdef in rdefs:
       
   258             execschemarql(execute, rdef,
       
   259                           rdef2rql(rdef, cstrtypemap, groupmap))
   246         if pb is not None:
   260         if pb is not None:
   247             pb.update()
   261             pb.update()
   248     for rql, kwargs in specialize2rql(schema):
   262     for rql, kwargs in specialize2rql(schema):
   249         assert execute(rql, kwargs, build_descr=False)
   263         assert execute(rql, kwargs, build_descr=False)
   250         if pb is not None:
   264         if pb is not None:
   251             pb.update()
   265             pb.update()
   252     if not quiet:
   266     if not quiet:
   253         print
   267         print
   254 
   268 
       
   269 
       
   270 # high level serialization functions
       
   271 
       
   272 def execschemarql(execute, schema, rqls):
       
   273     for rql, kwargs in rqls:
       
   274         kwargs['x'] = schema.eid
       
   275         rset = execute(rql, kwargs, build_descr=False)
       
   276         if schema.eid is None:
       
   277             schema.eid = rset[0][0]
       
   278         else:
       
   279             assert rset
       
   280 
       
   281 def erschema2rql(erschema, groupmap):
       
   282     if isinstance(erschema, schemamod.EntitySchema):
       
   283         return eschema2rql(erschema, groupmap=groupmap)
       
   284     return rschema2rql(erschema, groupmap=groupmap)
       
   285 
       
   286 def specialize2rql(schema):
       
   287     for eschema in schema.entities():
       
   288         if eschema.final:
       
   289             continue
       
   290         for rql, kwargs in eschemaspecialize2rql(eschema):
       
   291             yield rql, kwargs
       
   292 
       
   293 # etype serialization
       
   294 
       
   295 def eschema2rql(eschema, groupmap=None):
       
   296     """return a list of rql insert statements to enter an entity schema
       
   297     in the database as an CWEType entity
       
   298     """
       
   299     relations, values = eschema_relations_values(eschema)
       
   300     # NOTE: 'specializes' relation can't be inserted here since there's no
       
   301     # way to make sure the parent type is inserted before the child type
       
   302     yield 'INSERT CWEType X: %s' % ','.join(relations) , values
       
   303     # entity permissions
       
   304     if groupmap is not None:
       
   305         for rql, args in _erperms2rql(eschema, groupmap):
       
   306             yield rql, args
       
   307 
       
   308 def eschema_relations_values(eschema):
       
   309     values = _ervalues(eschema)
       
   310     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
       
   311     return relations, values
       
   312 
       
   313 def eschemaspecialize2rql(eschema):
       
   314     specialized_type = eschema.specializes()
       
   315     if specialized_type:
       
   316         values = {'x': eschema.eid, 'et': specialized_type.eid}
       
   317         yield 'SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', values
   255 
   318 
   256 def _ervalues(erschema):
   319 def _ervalues(erschema):
   257     try:
   320     try:
   258         type_ = unicode(erschema.type)
   321         type_ = unicode(erschema.type)
   259     except UnicodeDecodeError, e:
   322     except UnicodeDecodeError, e:
   266         'name': type_,
   329         'name': type_,
   267         'final': erschema.final,
   330         'final': erschema.final,
   268         'description': desc,
   331         'description': desc,
   269         }
   332         }
   270 
   333 
   271 def eschema_relations_values(eschema):
   334 # rtype serialization
   272     values = _ervalues(eschema)
   335 
   273     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
   336 def rschema2rql(rschema, cstrtypemap=None, addrdef=True, groupmap=None):
   274     return relations, values
   337     """return a list of rql insert statements to enter a relation schema
       
   338     in the database as an CWRType entity
       
   339     """
       
   340     if rschema.type == 'has_text':
       
   341         return
       
   342     relations, values = rschema_relations_values(rschema)
       
   343     yield 'INSERT CWRType X: %s' % ','.join(relations), values
       
   344     if addrdef:
       
   345         assert cstrtypemap
       
   346         # sort for testing purpose
       
   347         for rdef in sorted(rschema.rdefs.itervalues(),
       
   348                            key=lambda x: (x.subject, x.object)):
       
   349             for rql, values in rdef2rql(rdef, cstrtypemap, groupmap):
       
   350                 yield rql, values
   275 
   351 
   276 def rschema_relations_values(rschema):
   352 def rschema_relations_values(rschema):
   277     values = _ervalues(rschema)
   353     values = _ervalues(rschema)
   278     values['final'] = rschema.final
   354     values['final'] = rschema.final
   279     values['symmetric'] = rschema.symmetric
   355     values['symmetric'] = rschema.symmetric
   283     else:
   359     else:
   284         values['fulltext_container'] = rschema.fulltext_container
   360         values['fulltext_container'] = rschema.fulltext_container
   285     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
   361     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
   286     return relations, values
   362     return relations, values
   287 
   363 
   288 def _rdef_values(objtype, props):
   364 # rdef serialization
   289     amap = {'order': 'ordernum'}
   365 
       
   366 def rdef2rql(rdef, cstrtypemap, groupmap=None):
       
   367     # don't serialize infered relations
       
   368     if rdef.infered:
       
   369         return
       
   370     relations, values = _rdef_values(rdef)
       
   371     relations.append('X relation_type ER,X from_entity SE,X to_entity OE')
       
   372     values.update({'se': rdef.subject.eid, 'rt': rdef.rtype.eid, 'oe': rdef.object.eid})
       
   373     if rdef.final:
       
   374         etype = 'CWAttribute'
       
   375     else:
       
   376         etype = 'CWRelation'
       
   377     yield 'INSERT %s X: %s WHERE SE eid %%(se)s,ER eid %%(rt)s,OE eid %%(oe)s' % (
       
   378         etype, ','.join(relations), ), values
       
   379     for rql, values in constraints2rql(cstrtypemap, rdef.constraints):
       
   380         yield rql, values
       
   381     # no groupmap means "no security insertion"
       
   382     if groupmap:
       
   383         for rql, args in _erperms2rql(rdef, groupmap):
       
   384             yield rql, args
       
   385 
       
   386 def _rdef_values(rdef):
       
   387     amap = {'order': 'ordernum', 'default': 'defaultval'}
   290     values = {}
   388     values = {}
   291     for prop, default in schemamod.RelationDefinitionSchema.rproperty_defs(objtype).iteritems():
   389     for prop, default in rdef.rproperty_defs(rdef.object).iteritems():
   292         if prop in ('eid', 'constraints', 'uid', 'infered', 'permissions'):
   390         if prop in ('eid', 'constraints', 'uid', 'infered', 'permissions'):
   293             continue
   391             continue
   294         value = props.get(prop, default)
   392         value = getattr(rdef, prop)
       
   393         # XXX type cast really necessary?
   295         if prop in ('indexed', 'fulltextindexed', 'internationalizable'):
   394         if prop in ('indexed', 'fulltextindexed', 'internationalizable'):
   296             value = bool(value)
   395             value = bool(value)
   297         elif prop == 'ordernum':
   396         elif prop == 'ordernum':
   298             value = int(value)
   397             value = int(value)
   299         elif isinstance(value, str):
   398         elif isinstance(value, str):
   300             value = unicode(value)
   399             value = unicode(value)
       
   400         if value is not None and prop == 'default':
       
   401             if value is False:
       
   402                 value = u''
       
   403             if not isinstance(value, unicode):
       
   404                 value = unicode(value)
   301         values[amap.get(prop, prop)] = value
   405         values[amap.get(prop, prop)] = value
   302     return values
       
   303 
       
   304 def nfrdef_relations_values(objtype, props):
       
   305     values = _rdef_values(objtype, props)
       
   306     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
   406     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
   307     return relations, values
   407     return relations, values
   308 
   408 
   309 def frdef_relations_values(objtype, props):
   409 def constraints2rql(cstrtypemap, constraints, rdefeid=None):
   310     values = _rdef_values(objtype, props)
   410     for constraint in constraints:
   311     default = values['default']
   411         values = {'ct': cstrtypemap[constraint.type()],
   312     del values['default']
   412                   'value': unicode(constraint.serialize()),
   313     if default is not None:
   413                   'x': rdefeid} # when not specified, will have to be set by the caller
   314         if default is False:
   414         yield 'INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE \
   315             default = u''
   415 CT eid %(ct)s, EDEF eid %(x)s', values
   316         elif not isinstance(default, unicode):
       
   317             default = unicode(default)
       
   318     values['defaultval'] = default
       
   319     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
       
   320     return relations, values
       
   321 
       
   322 
       
   323 def __rdef2rql(genmap, rschema, subjtype=None, objtype=None, props=None,
       
   324                groupmap=None):
       
   325     if subjtype is None:
       
   326         assert objtype is None
       
   327         assert props is None
       
   328         targets = sorted(rschema.rdefs)
       
   329     else:
       
   330         assert not objtype is None
       
   331         targets = [(subjtype, objtype)]
       
   332     # relation schema
       
   333     if rschema.final:
       
   334         etype = 'CWAttribute'
       
   335     else:
       
   336         etype = 'CWRelation'
       
   337     for subjtype, objtype in targets:
       
   338         if props is None:
       
   339             _props = rschema.rdef(subjtype, objtype)
       
   340         else:
       
   341             _props = props
       
   342         # don't serialize infered relations
       
   343         if _props.get('infered'):
       
   344             continue
       
   345         gen = genmap[rschema.final]
       
   346         for rql, values in gen(rschema, subjtype, objtype, _props):
       
   347             yield rql, values
       
   348         # no groupmap means "no security insertion"
       
   349         if groupmap:
       
   350             for rql, args in _erperms2rql(_props, groupmap):
       
   351                 args['st'] = str(subjtype)
       
   352                 args['rt'] = str(rschema)
       
   353                 args['ot'] = str(objtype)
       
   354                 yield rql + 'X is %s, X from_entity ST, X to_entity OT, '\
       
   355                       'X relation_type RT, RT name %%(rt)s, ST name %%(st)s, '\
       
   356                       'OT name %%(ot)s' % etype, args
       
   357 
       
   358 
       
   359 def schema2rql(schema, skip=None, allow=None):
       
   360     """return a list of rql insert statements to enter the schema in the
       
   361     database as CWRType and CWEType entities
       
   362     """
       
   363     assert not (skip is not None and allow is not None), \
       
   364            'can\'t use both skip and allow'
       
   365     all = schema.entities() + schema.relations()
       
   366     if skip is not None:
       
   367         return chain(*[erschema2rql(schema[t]) for t in all if not t in skip])
       
   368     elif allow is not None:
       
   369         return chain(*[erschema2rql(schema[t]) for t in all if t in allow])
       
   370     return chain(*[erschema2rql(schema[t]) for t in all])
       
   371 
       
   372 def erschema2rql(erschema, groupmap):
       
   373     if isinstance(erschema, schemamod.EntitySchema):
       
   374         return eschema2rql(erschema, groupmap=groupmap)
       
   375     return rschema2rql(erschema, groupmap=groupmap)
       
   376 
       
   377 def eschema2rql(eschema, groupmap=None):
       
   378     """return a list of rql insert statements to enter an entity schema
       
   379     in the database as an CWEType entity
       
   380     """
       
   381     relations, values = eschema_relations_values(eschema)
       
   382     # NOTE: 'specializes' relation can't be inserted here since there's no
       
   383     # way to make sure the parent type is inserted before the child type
       
   384     yield 'INSERT CWEType X: %s' % ','.join(relations) , values
       
   385     # entity permissions
       
   386     if groupmap is not None:
       
   387         for rql, args in _erperms2rql(eschema, groupmap):
       
   388             args['name'] = str(eschema)
       
   389             yield rql + 'X is CWEType, X name %(name)s', args
       
   390 
       
   391 def specialize2rql(schema):
       
   392     for eschema in schema.entities():
       
   393         for rql, kwargs in eschemaspecialize2rql(eschema):
       
   394             yield rql, kwargs
       
   395 
       
   396 def eschemaspecialize2rql(eschema):
       
   397     specialized_type = eschema.specializes()
       
   398     if specialized_type:
       
   399         values = {'x': eschema.type, 'et': specialized_type.type}
       
   400         yield 'SET X specializes ET WHERE X name %(x)s, ET name %(et)s', values
       
   401 
       
   402 def rschema2rql(rschema, addrdef=True, groupmap=None):
       
   403     """return a list of rql insert statements to enter a relation schema
       
   404     in the database as an CWRType entity
       
   405     """
       
   406     if rschema.type == 'has_text':
       
   407         return
       
   408     relations, values = rschema_relations_values(rschema)
       
   409     yield 'INSERT CWRType X: %s' % ','.join(relations), values
       
   410     if addrdef:
       
   411         for rql, values in rdef2rql(rschema, groupmap=groupmap):
       
   412             yield rql, values
       
   413 
       
   414 def rdef2rql(rschema, subjtype=None, objtype=None, props=None, groupmap=None):
       
   415     genmap = {True: frdef2rql, False: nfrdef2rql}
       
   416     return __rdef2rql(genmap, rschema, subjtype, objtype, props, groupmap)
       
   417 
       
   418 
       
   419 _LOCATE_RDEF_RQL0 = 'X relation_type ER,X from_entity SE,X to_entity OE'
       
   420 _LOCATE_RDEF_RQL1 = 'SE name %(se)s,ER name %(rt)s,OE name %(oe)s'
       
   421 
       
   422 def frdef2rql(rschema, subjtype, objtype, props):
       
   423     relations, values = frdef_relations_values(objtype, props)
       
   424     relations.append(_LOCATE_RDEF_RQL0)
       
   425     values.update({'se': str(subjtype), 'rt': str(rschema), 'oe': str(objtype)})
       
   426     yield 'INSERT CWAttribute X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
       
   427     for rql, values in rdefrelations2rql(rschema, subjtype, objtype, props):
       
   428         yield rql + ', EDEF is CWAttribute', values
       
   429 
       
   430 def nfrdef2rql(rschema, subjtype, objtype, props):
       
   431     relations, values = nfrdef_relations_values(objtype, props)
       
   432     relations.append(_LOCATE_RDEF_RQL0)
       
   433     values.update({'se': str(subjtype), 'rt': str(rschema), 'oe': str(objtype)})
       
   434     yield 'INSERT CWRelation X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
       
   435     for rql, values in rdefrelations2rql(rschema, subjtype, objtype, props):
       
   436         yield rql + ', EDEF is CWRelation', values
       
   437 
       
   438 def rdefrelations2rql(rschema, subjtype, objtype, props):
       
   439     iterators = []
       
   440     for constraint in props.constraints:
       
   441         iterators.append(constraint2rql(rschema, subjtype, objtype, constraint))
       
   442     return chain(*iterators)
       
   443 
       
   444 def constraint2rql(rschema, subjtype, objtype, constraint):
       
   445     values = {'ctname': unicode(constraint.type()),
       
   446               'value': unicode(constraint.serialize()),
       
   447               'rt': str(rschema), 'se': str(subjtype), 'oe': str(objtype)}
       
   448     yield 'INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE \
       
   449 CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, \
       
   450 ER name %(rt)s, SE name %(se)s, OE name %(oe)s', values
       
   451 
   416 
   452 
   417 
   453 def _erperms2rql(erschema, groupmap):
   418 def _erperms2rql(erschema, groupmap):
   454     """return rql insert statements to enter the entity or relation
   419     """return rql insert statements to enter the entity or relation
   455     schema's permissions in the database as
   420     schema's permissions in the database as
   464             continue
   429             continue
   465         for group_or_rqlexpr in grantedto:
   430         for group_or_rqlexpr in grantedto:
   466             if isinstance(group_or_rqlexpr, basestring):
   431             if isinstance(group_or_rqlexpr, basestring):
   467                 # group
   432                 # group
   468                 try:
   433                 try:
   469                     yield ('SET X %s_permission Y WHERE Y eid %%(g)s, ' % action,
   434                     yield ('SET X %s_permission Y WHERE Y eid %%(g)s, X eid %%(x)s' % action,
   470                            {'g': groupmap[group_or_rqlexpr]})
   435                            {'g': groupmap[group_or_rqlexpr]})
   471                 except KeyError:
   436                 except KeyError:
   472                     continue
   437                     continue
   473             else:
   438             else:
   474                 # rqlexpr
   439                 # rqlexpr
   475                 rqlexpr = group_or_rqlexpr
   440                 rqlexpr = group_or_rqlexpr
   476                 yield ('INSERT RQLExpression E: E expression %%(e)s, E exprtype %%(t)s, '
   441                 yield ('INSERT RQLExpression E: E expression %%(e)s, E exprtype %%(t)s, '
   477                        'E mainvars %%(v)s, X %s_permission E WHERE ' % action,
   442                        'E mainvars %%(v)s, X %s_permission E WHERE X eid %%(x)s' % action,
   478                        {'e': unicode(rqlexpr.expression),
   443                        {'e': unicode(rqlexpr.expression),
   479                         'v': unicode(rqlexpr.mainvars),
   444                         'v': unicode(rqlexpr.mainvars),
   480                         't': unicode(rqlexpr.__class__.__name__)})
   445                         't': unicode(rqlexpr.__class__.__name__)})
   481 
   446 
   482 
   447 # update functions
   483 def updateeschema2rql(eschema):
   448 
       
   449 def updateeschema2rql(eschema, eid):
   484     relations, values = eschema_relations_values(eschema)
   450     relations, values = eschema_relations_values(eschema)
   485     values['et'] = eschema.type
   451     values['x'] = eid
   486     yield 'SET %s WHERE X is CWEType, X name %%(et)s' % ','.join(relations), values
   452     yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values
   487 
   453 
   488 def updaterschema2rql(rschema):
   454 def updaterschema2rql(rschema, eid):
   489     relations, values = rschema_relations_values(rschema)
   455     relations, values = rschema_relations_values(rschema)
   490     values['rt'] = rschema.type
   456     values['x'] = eid
   491     yield 'SET %s WHERE X is CWRType, X name %%(rt)s' % ','.join(relations), values
   457     yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values
   492 
   458 
   493 def updaterdef2rql(rschema, subjtype=None, objtype=None, props=None):
   459 def updaterdef2rql(rdef, eid):
   494     genmap = {True: updatefrdef2rql, False: updatenfrdef2rql}
   460     relations, values = _rdef_values(rdef)
   495     return __rdef2rql(genmap, rschema, subjtype, objtype, props)
   461     values['x'] = eid
   496 
   462     yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values
   497 def updatefrdef2rql(rschema, subjtype, objtype, props):
       
   498     relations, values = frdef_relations_values(objtype, props)
       
   499     values.update({'se': subjtype, 'rt': str(rschema), 'oe': objtype})
       
   500     yield 'SET %s WHERE %s, %s, X is CWAttribute' % (','.join(relations),
       
   501                                                      _LOCATE_RDEF_RQL0,
       
   502                                                      _LOCATE_RDEF_RQL1), values
       
   503 
       
   504 def updatenfrdef2rql(rschema, subjtype, objtype, props):
       
   505     relations, values = nfrdef_relations_values(objtype, props)
       
   506     values.update({'se': subjtype, 'rt': str(rschema), 'oe': objtype})
       
   507     yield 'SET %s WHERE %s, %s, X is CWRelation' % (','.join(relations),
       
   508                                                     _LOCATE_RDEF_RQL0,
       
   509                                                     _LOCATE_RDEF_RQL1), values