# HG changeset patch # User Laura Médioni # Date 1409205735 -7200 # Node ID 5531f5577b50b846bd7d0d8fbbc61b12f6aa10d4 # Parent 64b573d54133461e8e7f02cd95f9d2912c1a85fb [CWEP002 migration] support add_relation_type for computed relations Related to #3546717. diff -r 64b573d54133 -r 5531f5577b50 hooks/syncschema.py --- a/hooks/syncschema.py Thu Aug 28 07:55:33 2014 +0200 +++ b/hooks/syncschema.py Thu Aug 28 08:02:15 2014 +0200 @@ -985,6 +985,25 @@ MemSchemaCWRTypeDel(self._cw, rtype=name) +class AfterAddCWComputedRTypeHook(SyncSchemaHook): + """after a CWComputedRType entity has been added: + * register an operation to add the relation type to the instance's + schema on commit + + We don't know yet this point if a table is necessary + """ + __regid__ = 'syncaddcwcomputedrtype' + __select__ = SyncSchemaHook.__select__ & is_instance('CWComputedRType') + events = ('after_add_entity',) + + def __call__(self): + entity = self.entity + rtypedef = ybo.ComputedRelation(name=entity.name, + eid=entity.eid, + rule=entity.rule) + MemSchemaCWRTypeAdd(self._cw, rtypedef=rtypedef) + + class AfterAddCWRTypeHook(SyncSchemaHook): """after a CWRType entity has been added: * register an operation to add the relation type to the instance's diff -r 64b573d54133 -r 5531f5577b50 schema.py --- a/schema.py Thu Aug 28 07:55:33 2014 +0200 +++ b/schema.py Thu Aug 28 08:02:15 2014 +0200 @@ -1039,6 +1039,10 @@ rdef.infered = True self.add_relation_def(rdef) + def rebuild_infered_relations(self): + super(CubicWebSchema, self).rebuild_infered_relations() + self.finalize_computed_relations() + # additional cw specific constraints ########################################### diff -r 64b573d54133 -r 5531f5577b50 server/migractions.py --- a/server/migractions.py Thu Aug 28 07:55:33 2014 +0200 +++ b/server/migractions.py Thu Aug 28 08:02:15 2014 +0200 @@ -1018,11 +1018,13 @@ if rtype in reposchema: print 'warning: relation type %s is already known, skip addition' % ( rtype) + elif rschema.rule: + ss.execschemarql(execute, rschema, ss.crschema2rql(rschema)) else: # register the relation into CWRType and insert necessary relation # definitions ss.execschemarql(execute, rschema, ss.rschema2rql(rschema, addrdef=False)) - if addrdef: + if not rschema.rule and addrdef: self.commit() gmap = self.group_mapping() cmap = self.cstrtype_mapping() diff -r 64b573d54133 -r 5531f5577b50 server/schemaserial.py --- a/server/schemaserial.py Thu Aug 28 07:55:33 2014 +0200 +++ b/server/schemaserial.py Thu Aug 28 08:02:15 2014 +0200 @@ -362,6 +362,8 @@ continue if rschema.rule: execschemarql(execute, rschema, crschema2rql(rschema)) + if pb is not None: + pb.update() continue execschemarql(execute, rschema, rschema2rql(rschema, addrdef=False)) if rschema.symmetric: @@ -511,7 +513,7 @@ def crschema_relations_values(crschema): values = _ervalues(crschema) - values['rule'] = crschema.rule + values['rule'] = unicode(crschema.rule) # XXX why oh why? del values['final'] relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] diff -r 64b573d54133 -r 5531f5577b50 server/test/unittest_migractions.py --- a/server/test/unittest_migractions.py Thu Aug 28 07:55:33 2014 +0200 +++ b/server/test/unittest_migractions.py Thu Aug 28 08:02:15 2014 +0200 @@ -77,6 +77,19 @@ repo=self.repo, cnx=cnx, interactive=False) + def table_sql(self, mh, tablename): + result = mh.sqlexec("SELECT sql FROM sqlite_master WHERE type='table' " + "and name=%(table)s", {'table': tablename}) + if result: + return result[0][0] + return None # no such table + + def table_schema(self, mh, tablename): + sql = self.table_sql(mh, tablename) + assert sql, 'no table %s' % tablename + return dict(x.split()[:2] + for x in sql.split('(', 1)[1].rsplit(')', 1)[0].split(',')) + class MigrationCommandsTC(MigrationTC): @@ -146,8 +159,7 @@ self.assertEqual(self.schema['shortpara'].subjects(), ('Note', )) self.assertEqual(self.schema['shortpara'].objects(), ('String', )) # test created column is actually a varchar(64) - notesql = mh.sqlexec("SELECT sql FROM sqlite_master WHERE type='table' and name='%sNote'" % SQL_PREFIX)[0][0] - fields = dict(x.strip().split()[:2] for x in notesql.split('(', 1)[1].rsplit(')', 1)[0].split(',')) + fields = self.table_schema(mh, '%sNote' % SQL_PREFIX) self.assertEqual(fields['%sshortpara' % SQL_PREFIX], 'varchar(64)') # test default value set on existing entities self.assertEqual(cnx.execute('Note X').get_entity(0, 0).shortpara, 'hop') @@ -667,16 +679,11 @@ self.assertEqual(self.schema['Note'].specializes(), None) self.assertEqual(self.schema['Text'].specializes(), None) - def test_add_symmetric_relation_type(self): with self.mh() as (cnx, mh): - same_as_sql = mh.sqlexec("SELECT sql FROM sqlite_master WHERE type='table' " - "and name='same_as_relation'") - self.assertFalse(same_as_sql) + self.assertFalse(self.table_sql(mh, 'same_as_relation')) mh.cmd_add_relation_type('same_as') - same_as_sql = mh.sqlexec("SELECT sql FROM sqlite_master WHERE type='table' " - "and name='same_as_relation'") - self.assertTrue(same_as_sql) + self.assertTrue(self.table_sql(mh, 'same_as_relation')) class MigrationCommandsComputedTC(MigrationTC): @@ -703,6 +710,25 @@ 'Cannot drop a relation definition for a computed ' 'relation (notes)') + def test_computed_relation_add_relation_type(self): + self.assertNotIn('works_for', self.schema) + with self.mh() as (cnx, mh): + mh.cmd_add_relation_type('works_for') + self.assertIn('works_for', self.schema) + self.assertEqual(self.schema['works_for'].rule, + 'O employees S, NOT EXISTS (O associates S)') + self.assertEqual(self.schema['works_for'].objects(), ('Company',)) + self.assertEqual(self.schema['works_for'].subjects(), ('Employee',)) + self.assertFalse(self.table_sql(mh, 'works_for_relation')) + e = cnx.create_entity('Employee') + a = cnx.create_entity('Employee') + cnx.create_entity('Company', employees=e, associates=a) + cnx.commit() + company = cnx.execute('Company X').get_entity(0, 0) + self.assertEqual([e.eid], + [x.eid for x in company.reverse_works_for]) + mh.rollback() + def test_computed_relation_drop_relation_type(self): self.assertIn('notes', self.schema) with self.mh() as (cnx, mh):