[schema sync] Rename index when an entity type is renamed
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 20 Jun 2016 18:00:00 +0200
changeset 11362 ebe75d73acdd
parent 11361 5a857bba1b79
child 11363 e5fe836df6f1
[schema sync] Rename index when an entity type is renamed So we may still find them later using their expected name, and avoid collisions. Closes #13822045
cubicweb/hooks/syncschema.py
cubicweb/server/schema2sql.py
cubicweb/server/test/data-migractions/migratedapp/schema.py
cubicweb/server/test/data-migractions/schema.py
cubicweb/server/test/unittest_migractions.py
--- a/cubicweb/hooks/syncschema.py	Fri Jun 17 13:26:13 2016 +0200
+++ b/cubicweb/hooks/syncschema.py	Mon Jun 20 18:00:00 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -31,6 +31,7 @@
 from hashlib import md5
 
 from yams.schema import BASE_TYPES, BadSchemaDefinition, RelationDefinitionSchema
+from yams.constraints import UniqueConstraint
 from yams import buildobjs as ybo, convert_default_value
 
 from logilab.common.decorators import clear_cache
@@ -41,6 +42,7 @@
                              CONSTRAINTS, UNIQUE_CONSTRAINTS, ETYPE_NAME_MAP)
 from cubicweb.server import hook, schemaserial as ss, schema2sql as y2sql
 from cubicweb.server.sqlutils import SQL_PREFIX
+from cubicweb.server.schema2sql import unique_index_name
 from cubicweb.hooks.synccomputed import RecomputeAttributeOperation
 
 # core entity and relation types which can't be removed
@@ -291,19 +293,47 @@
     oldname = newname = None  # make pylint happy
 
     def rename(self, oldname, newname):
-        self.cnx.vreg.schema.rename_entity_type(oldname, newname)
+        cnx = self.cnx
+        source = cnx.repo.system_source
+        dbhelper = source.dbhelper
         # we need sql to operate physical changes on the system database
-        sqlexec = self.cnx.system_sql
-        dbhelper = self.cnx.repo.system_source.dbhelper
-        sql = dbhelper.sql_rename_table(SQL_PREFIX+oldname,
-                                        SQL_PREFIX+newname)
+        sqlexec = cnx.system_sql
+        cnx.vreg.schema.rename_entity_type(oldname, newname)
+        old_table = SQL_PREFIX + oldname
+        new_table = SQL_PREFIX + newname
+        eschema = cnx.vreg.schema.eschema(newname)
+        # drop old indexes before the renaming
+        for rschema in eschema.subject_relations():
+            if rschema.inlined or (rschema.final and eschema.rdef(rschema.type).indexed):
+                source.drop_index(cnx, old_table, SQL_PREFIX + rschema.type)
+            if rschema.final and any(isinstance(cstr, UniqueConstraint)
+                                     for cstr in eschema.rdef(rschema.type).constraints):
+                source.drop_index(cnx, old_table, SQL_PREFIX + rschema.type, unique=True)
+        sql = dbhelper.sql_rename_table(old_table, new_table)
         sqlexec(sql)
         self.info('renamed table %s to %s', oldname, newname)
         sqlexec('UPDATE entities SET type=%(newname)s WHERE type=%(oldname)s',
                 {'newname': newname, 'oldname': oldname})
-        for eid, (etype, extid, auri) in self.cnx.repo._type_source_cache.items():
+        for eid, (etype, extid, auri) in cnx.repo._type_source_cache.items():
             if etype == oldname:
-                self.cnx.repo._type_source_cache[eid] = (newname, extid, auri)
+                cnx.repo._type_source_cache[eid] = (newname, extid, auri)
+        # recreate the indexes
+        for rschema in eschema.subject_relations():
+            if rschema.inlined or (rschema.final and eschema.rdef(rschema.type).indexed):
+                source.create_index(cnx, new_table, SQL_PREFIX + rschema.type)
+            if rschema.final and any(isinstance(cstr, UniqueConstraint)
+                                     for cstr in eschema.rdef(rschema.type).constraints):
+                source.create_index(cnx, new_table, SQL_PREFIX + rschema.type, unique=True)
+        for attrs in eschema._unique_together or ():
+            columns = ['%s%s' % (SQL_PREFIX, attr) for attr in attrs]
+            old_index_name = unique_index_name(oldname, columns)
+            for sql in dbhelper.sqls_drop_multicol_unique_index(
+                    new_table, columns, old_index_name):
+                sqlexec(sql)
+            new_index_name = unique_index_name(newname, columns)
+            for sql in dbhelper.sqls_create_multicol_unique_index(
+                    new_table, columns, new_index_name):
+                sqlexec(sql)
         # XXX transaction records
 
     def precommit_event(self):
--- a/cubicweb/server/schema2sql.py	Fri Jun 17 13:26:13 2016 +0200
+++ b/cubicweb/server/schema2sql.py	Mon Jun 20 18:00:00 2016 +0200
@@ -111,8 +111,10 @@
 
 
 def unique_index_name(eschema, columns):
+    # keep giving eschema instead of table name for bw compat
+    table = text_type(eschema)
     # unique_index_name is used as name of CWUniqueConstraint, hence it should be unicode
-    return text_type(build_index_name(eschema.type, columns, 'unique_'))
+    return text_type(build_index_name(table, columns, 'unique_'))
 
 
 def iter_unique_index_names(eschema):
--- a/cubicweb/server/test/data-migractions/migratedapp/schema.py	Fri Jun 17 13:26:13 2016 +0200
+++ b/cubicweb/server/test/data-migractions/migratedapp/schema.py	Mon Jun 20 18:00:00 2016 +0200
@@ -205,7 +205,7 @@
 
 # New
 class New(EntityType):
-    new_name = String()
+    new_name = String(indexed=True, unique=True)
 
 # New
 class same_as(RelationDefinition):
--- a/cubicweb/server/test/data-migractions/schema.py	Fri Jun 17 13:26:13 2016 +0200
+++ b/cubicweb/server/test/data-migractions/schema.py	Mon Jun 20 18:00:00 2016 +0200
@@ -162,7 +162,7 @@
         'read'   : ('managers', 'users', 'guests'),
         'add'    : ('managers', 'users', 'guests'),
         'update' : ()
-    })
+    }, indexed=True, unique=True)
 
 
 class connait(RelationType):
--- a/cubicweb/server/test/unittest_migractions.py	Fri Jun 17 13:26:13 2016 +0200
+++ b/cubicweb/server/test/unittest_migractions.py	Mon Jun 20 18:00:00 2016 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -35,6 +35,7 @@
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.migractions import ServerMigrationHelper
 from cubicweb.server.sources import storages
+from cubicweb.server.schema2sql import build_index_name
 
 import cubicweb.devtools
 
@@ -340,7 +341,18 @@
             entity = mh.create_entity('Old', name=u'old')
             self.repo.type_and_source_from_eid(entity.eid, entity._cw)
             mh.cmd_rename_entity_type('Old', 'New')
+            dbh = self.repo.system_source.dbhelper
+            indices = set(dbh.list_indices(cnx.cnxset.cu, 'cw_New'))
+            self.assertNotIn(build_index_name('cw_Old', ['cw_name']), indices)
+            self.assertIn(build_index_name('cw_New', ['cw_name']), indices)
             mh.cmd_rename_attribute('New', 'name', 'new_name')
+            indices = set(dbh.list_indices(cnx.cnxset.cu, 'cw_New'))
+            # check 'indexed' index
+            self.assertNotIn(build_index_name('cw_New', ['cw_name'], 'idx_'), indices)
+            self.assertIn(build_index_name('cw_New', ['cw_new_name'], 'idx_'), indices)
+            # check 'unique' index
+            self.assertNotIn(build_index_name('cw_New', ['cw_name'], 'key_'), indices)
+            self.assertIn(build_index_name('cw_New', ['cw_new_name'], 'key_'), indices)
 
     def test_add_drop_relation_type(self):
         with self.mh() as (cnx, mh):