[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
--- 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):