# HG changeset patch # User Sylvain Thénault # Date 1484748267 -3600 # Node ID e760c54490b13e1cd0fa5a818a2d34b4521894c0 # Parent 6f36275a6e7418871c8e6109282e9f057fb6a8a4 [migration] Fix addition of entity type including boundary constraints on its own attributes This was failing because of the sequence of sql executing when adding an attribute: 1. add entity type 2. add relation type for attribute 1 3. add attribute 1 and associated constraints etc. In the case of e.g. start/end constraint, we were trying to add the constraint before addition of the constrained attribute (e.g. add constraint on 'start' referencing 'end', but only 'start' has been added yet, not 'end'). This patch fix this by: * adding the relation type to the schema without having to commit, but keeping the operation to revert the addition if necessary - this allows to a single commit for all attributes of the entity type ; * using a LateOperation on constraint operation, so we ensure attributes are actually added before any constraint is added. diff -r 6f36275a6e74 -r e760c54490b1 cubicweb/hooks/syncschema.py --- a/cubicweb/hooks/syncschema.py Thu Jan 19 09:53:31 2017 +0100 +++ b/cubicweb/hooks/syncschema.py Wed Jan 18 15:04:27 2017 +0100 @@ -778,7 +778,7 @@ syssource.update_rdef_unique(self.cnx, self.rdef) -class CWConstraintAddOp(CWConstraintDelOp): +class CWConstraintAddOp(hook.LateOperation, CWConstraintDelOp): """actually update constraint of a relation definition""" entity = None # make pylint happy @@ -886,12 +886,10 @@ class MemSchemaCWRTypeAdd(MemSchemaOperation): - """actually add the relation type to the instance's schema""" + """Revert addition of the relation type from the instance's schema if something goes wrong. + """ rtypedef = None # make pylint happy - def precommit_event(self): - self.cnx.vreg.schema.add_relation_type(self.rtypedef) - def revertprecommit_event(self): self.cnx.vreg.schema.del_relation_type(self.rtypedef.name) @@ -1101,6 +1099,10 @@ def __call__(self): rtypedef = self.rtype_def() + # modify the instance's schema now since we'll usually need the type definition to do + # further thing (e.g. add relation def of this type) but register and operation to revert + # this if necessary + self._cw.vreg.schema.add_relation_type(rtypedef) MemSchemaCWRTypeAdd(self._cw, rtypedef=rtypedef) def rtype_def(self): diff -r 6f36275a6e74 -r e760c54490b1 cubicweb/server/migractions.py --- a/cubicweb/server/migractions.py Thu Jan 19 09:53:31 2017 +0100 +++ b/cubicweb/server/migractions.py Wed Jan 18 15:04:27 2017 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -814,12 +814,12 @@ if attrschema.type not in instschema: self.cmd_add_entity_type(attrschema.type, False, False) if rschema.type not in instschema: - # need to add the relation type and to commit to get it - # actually in the schema - self.cmd_add_relation_type(rschema.type, False, commit=True) + # need to add the relation type + self.cmd_add_relation_type(rschema.type, False, commit=False) # register relation definition rdef = self._get_rdef(rschema, eschema, eschema.destination(rschema)) ss.execschemarql(execute, rdef, ss.rdef2rql(rdef, cstrtypemap, groupmap),) + self.commit() # take care to newly introduced base class # XXX some part of this should probably be under the "if auto" block for spschema in eschema.specialized_by(recursive=False): diff -r 6f36275a6e74 -r e760c54490b1 cubicweb/server/test/data-migractions/migratedapp/schema.py --- a/cubicweb/server/test/data-migractions/migratedapp/schema.py Thu Jan 19 09:53:31 2017 +0100 +++ b/cubicweb/server/test/data-migractions/migratedapp/schema.py Wed Jan 18 15:04:27 2017 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -20,7 +20,7 @@ from yams.buildobjs import (EntityType, RelationType, RelationDefinition, SubjectRelation, Bytes, RichString, String, Int, Boolean, Datetime, Date, Float) -from yams.constraints import SizeConstraint, UniqueConstraint +from yams.constraints import SizeConstraint, UniqueConstraint, BoundaryConstraint, Attribute from cubicweb import _ from cubicweb.schema import (WorkflowableEntityType, RQLConstraint, RQLVocabularyConstraint, @@ -216,3 +216,10 @@ subject = object = 'Folder2' inlined = True cardinality = '??' + + +class Activity(EntityType): + start = Datetime(constraints=[BoundaryConstraint('<=', Attribute('end'))], + description=_('when the activity started')) + end = Datetime(constraints=[BoundaryConstraint('>=', Attribute('start'))], + description=_('when the activity ended')) diff -r 6f36275a6e74 -r e760c54490b1 cubicweb/server/test/unittest_migractions.py --- a/cubicweb/server/test/unittest_migractions.py Thu Jan 19 09:53:31 2017 +0100 +++ b/cubicweb/server/test/unittest_migractions.py Wed Jan 18 15:04:27 2017 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -313,6 +313,12 @@ for cstr in eschema.rdef('name').constraints: self.assertTrue(hasattr(cstr, 'eid')) + def test_add_entity_type_with_constraint(self): + with self.mh() as (cnx, mh): + mh.cmd_add_entity_type('Activity') + constraints = self.table_constraints(mh, 'cw_Activity') + self.assertEqual(len(constraints), 2, constraints) + def test_add_cube_with_custom_final_type(self): with self.mh() as (cnx, mh): try: