[CWEP002 migration] support add_relation_type for computed relations
Related to #3546717.
--- 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
--- 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 ###########################################
--- 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()
--- 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)]
--- 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):