--- a/server/schemaserial.py Tue Sep 07 17:34:42 2010 +0200
+++ b/server/schemaserial.py Mon Sep 13 15:15:21 2010 +0200
@@ -24,7 +24,7 @@
from logilab.common.shellutils import ProgressBar
-from yams import schema as schemamod, buildobjs as ybo
+from yams import BadSchemaDefinition, schema as schemamod, buildobjs as ybo
from cubicweb import CW_SOFTWARE_ROOT, typed_eid
from cubicweb.schema import (CONSTRAINTS, ETYPE_NAME_MAP,
@@ -87,7 +87,7 @@
"""
repo = session.repo
dbhelper = repo.system_source.dbhelper
- # 3.6 migration
+ # XXX bw compat (3.6 migration)
sqlcu = session.pool['system']
sqlcu.execute("SELECT * FROM cw_CWRType WHERE cw_name='symetric'")
if sqlcu.fetchall():
@@ -95,8 +95,10 @@
dbhelper.TYPE_MAPPING['Boolean'], True)
sqlcu.execute(sql)
sqlcu.execute("UPDATE cw_CWRType SET cw_name='symmetric' WHERE cw_name='symetric'")
- sidx = {}
- permsdict = deserialize_ertype_permissions(session)
+ session.commit(False)
+ ertidx = {}
+ copiedeids = set()
+ permsidx = deserialize_ertype_permissions(session)
schema.reading_from_database = True
for eid, etype, desc in session.execute(
'Any X, N, D WHERE X is CWEType, X name N, X description D',
@@ -106,7 +108,7 @@
# just set the eid
eschema = schema.eschema(etype)
eschema.eid = eid
- sidx[eid] = eschema
+ ertidx[eid] = etype
continue
if etype in ETYPE_NAME_MAP:
needcopy = False
@@ -115,7 +117,8 @@
sqlexec = session.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 merge
+ # 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:
@@ -139,16 +142,16 @@
repo.clear_caches(tocleanup)
session.commit(False)
if needcopy:
- from logilab.common.testlib import mock_object
- sidx[eid] = mock_object(type=netype)
+ 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
- etype = ybo.EntityType(name=etype, description=desc, eid=eid)
- eschema = schema.add_entity_type(etype)
- sidx[eid] = eschema
- set_perms(eschema, permsdict)
+ ertidx[eid] = etype
+ eschema = schema.add_entity_type(
+ ybo.EntityType(name=etype, description=desc, eid=eid))
+ set_perms(eschema, permsidx)
for etype, stype in session.execute(
'Any XN, ETN WHERE X is CWEType, X name XN, X specializes ET, ET name ETN',
build_descr=False):
@@ -159,43 +162,81 @@
for eid, rtype, desc, sym, il, ftc in session.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):
- rtype = ybo.RelationType(name=rtype, description=desc,
- symmetric=bool(sym), inlined=bool(il),
- fulltext_container=ftc, eid=eid)
- rschema = schema.add_relation_type(rtype)
- sidx[eid] = rschema
- cstrsdict = deserialize_rdef_constraints(session)
+ 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))
+ cstrsidx = deserialize_rdef_constraints(session)
+ 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)
+
for values in session.execute(
'Any X,SE,RT,OE,CARD,ORD,DESC,IDX,FTIDX,I18N,DFLT 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,'
'X fulltextindexed FTIDX, X from_entity SE, X to_entity OE',
build_descr=False):
- rdefeid, seid, reid, teid, card, ord, desc, idx, ftidx, i18n, default = values
- rdef = ybo.RelationDefinition(sidx[seid].type, sidx[reid].type, sidx[teid].type,
- cardinality=card,
- constraints=cstrsdict.get(rdefeid, ()),
- order=ord, description=desc,
- indexed=idx, fulltextindexed=ftidx,
- internationalizable=i18n,
- default=default, eid=rdefeid)
- rdefs = schema.add_relation_def(rdef)
- # rdefs can be None on duplicated relation definitions (e.g. symmetrics)
- if rdefs is not None:
- set_perms(rdefs, permsdict)
+ rdefeid, seid, reid, oeid, card, ord, desc, idx, ftidx, i18n, default = values
+ _add_rdef(rdefeid, seid, reid, oeid,
+ cardinality=card, description=desc, order=ord,
+ indexed=idx, fulltextindexed=ftidx, internationalizable=i18n,
+ default=default)
for values in session.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, teid, card, ord, desc, c = values
- rdef = ybo.RelationDefinition(sidx[seid].type, sidx[reid].type, sidx[teid].type,
- constraints=cstrsdict.get(rdefeid, ()),
- cardinality=card, order=ord, description=desc,
- composite=c, eid=rdefeid)
- rdefs = schema.add_relation_def(rdef)
- # rdefs can be None on duplicated relation definitions (e.g. symmetrics)
+ 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, permsdict)
+ set_perms(rdefs, permsidx)
+ unique_togethers = {}
+ try:
+ rset = session.execute(
+ 'Any X,E,R WHERE '
+ 'X is CWUniqueTogetherConstraint, '
+ 'X constraint_of E, X relations R', build_descr=False)
+ except Exception:
+ session.rollback() # first migration introducing CWUniqueTogetherConstraint cw 3.9.6
+ else:
+ for values in rset:
+ uniquecstreid, eeid, releid = values
+ eschema = schema.schema_by_eid(eeid)
+ relations = unique_togethers.setdefault(uniquecstreid, (eschema, []))
+ relations[1].append(ertidx[releid].rtype.type)
+ for eschema, unique_together in unique_togethers.itervalues():
+ eschema._unique_together.append(tuple(sorted(unique_together)))
schema.infer_specialization_rules()
session.commit()
schema.reading_from_database = False
@@ -232,7 +273,7 @@
res.setdefault(rdefeid, []).append(cstr)
return res
-def set_perms(erschema, permsdict):
+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
@@ -240,7 +281,7 @@
# reset erschema permissions here to avoid getting yams default anyway
erschema.permissions = dict((action, ()) for action in erschema.ACTIONS)
try:
- thispermsdict = permsdict[erschema.eid]
+ thispermsdict = permsidx[erschema.eid]
except KeyError:
return
for action, somethings in thispermsdict.iteritems():
@@ -309,6 +350,10 @@
rdef2rql(rdef, cstrtypemap, groupmap))
if pb is not None:
pb.update()
+ # serialize unique_together constraints
+ for eschema in eschemas:
+ for unique_together in eschema._unique_together:
+ execschemarql(execute, eschema, [uniquetogether2rql(eschema, unique_together)])
for rql, kwargs in specialize2rql(schema):
execute(rql, kwargs, build_descr=False)
if pb is not None:
@@ -366,6 +411,31 @@
values = {'x': eschema.eid, 'et': specialized_type.eid}
yield 'SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', values
+def uniquetogether2rql(eschema, unique_together):
+ relations = []
+ restrictions = []
+ substs = {}
+ for i, name in enumerate(unique_together):
+ rschema = eschema.rdef(name)
+ var = 'R%d' % i
+ rtype = 'T%d' % i
+ substs[rtype] = rschema.rtype.type
+ relations.append('C relations %s' % var)
+ restrictions.append('%(var)s from_entity X, '
+ '%(var)s relation_type %(rtype)s, '
+ '%(rtype)s name %%(%(rtype)s)s' \
+ % {'var': var,
+ 'rtype':rtype})
+ relations = ', '.join(relations)
+ restrictions = ', '.join(restrictions)
+ rql = ('INSERT CWUniqueTogetherConstraint C: '
+ ' C constraint_of X, %s '
+ 'WHERE '
+ ' X eid %%(x)s, %s' )
+
+ return rql % (relations, restrictions), substs
+
+
def _ervalues(erschema):
try:
type_ = unicode(erschema.type)