[schema] Add a method on yams constraints to compute its unique name
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 08 Jul 2016 10:17:42 +0200
changeset 11416 9c2fbb872e91
parent 11415 f87da59faea1
child 11417 5e5e224239c3
[schema] Add a method on yams constraints to compute its unique name used to identify it in the DB backend.
cubicweb/entities/adapters.py
cubicweb/hooks/syncschema.py
cubicweb/schema.py
cubicweb/server/schema2sql.py
cubicweb/server/test/unittest_migractions.py
--- a/cubicweb/entities/adapters.py	Fri Jul 08 10:17:14 2016 +0200
+++ b/cubicweb/entities/adapters.py	Fri Jul 08 10:17:42 2016 +0200
@@ -21,7 +21,6 @@
 from cubicweb import _
 
 from itertools import chain
-from hashlib import md5
 
 from logilab.mtconverter import TransformError
 from logilab.common.decorators import cached
@@ -413,9 +412,7 @@
         for rschema, attrschema in eschema.attribute_definitions():
             rdef = rschema.rdef(eschema, attrschema)
             for constraint in rdef.constraints:
-                if cstrname == 'cstr' + md5(
-                        (eschema.type + rschema.type + constraint.type() +
-                         (constraint.serialize() or '')).encode('ascii')).hexdigest():
+                if cstrname == constraint.name_for(rdef):
                     break
             else:
                 continue
--- a/cubicweb/hooks/syncschema.py	Fri Jul 08 10:17:14 2016 +0200
+++ b/cubicweb/hooks/syncschema.py	Fri Jul 08 10:17:42 2016 +0200
@@ -28,7 +28,6 @@
 
 import json
 from copy import copy
-from hashlib import md5
 
 from yams.schema import BASE_TYPES, BadSchemaDefinition, RelationDefinitionSchema
 from yams.constraints import UniqueConstraint
@@ -754,10 +753,11 @@
         elif cstrtype == 'UniqueConstraint':
             syssource.update_rdef_unique(cnx, rdef)
             self.unique_changed = True
-        if cstrtype in ('BoundaryConstraint', 'IntervalBoundConstraint', 'StaticVocabularyConstraint'):
-            cstrname = 'cstr' + md5((rdef.subject.type + rdef.rtype.type + cstrtype +
-                                     (self.oldcstr.serialize() or '')).encode('utf-8')).hexdigest()
-            cnx.system_sql('ALTER TABLE %s%s DROP CONSTRAINT %s' % (SQL_PREFIX, rdef.subject.type, cstrname))
+        elif cstrtype in ('BoundaryConstraint',
+                          'IntervalBoundConstraint',
+                          'StaticVocabularyConstraint'):
+            cnx.system_sql('ALTER TABLE %s%s DROP CONSTRAINT %s'
+                           % (SQL_PREFIX, rdef.subject, self.oldcstr.name_for(rdef)))
 
     def revertprecommit_event(self):
         # revert changes on in memory schema
@@ -811,13 +811,12 @@
             # oldcstr is the new constraint when the attribute is being added in the same
             # transaction or when constraint value is updated. So we've to take care...
             if oldcstr is not None:
-                oldcstrname = 'cstr' + md5((rdef.subject.type + rdef.rtype.type + cstrtype +
-                                            (self.oldcstr.serialize() or '')).encode('utf-8')).hexdigest()
+                oldcstrname = self.oldcstr.name_for(rdef)
                 if oldcstrname != cstrname:
                     cnx.system_sql('ALTER TABLE %s%s DROP CONSTRAINT %s'
-                                   % (SQL_PREFIX, rdef.subject.type, oldcstrname))
+                                   % (SQL_PREFIX, rdef.subject, oldcstrname))
             cnx.system_sql('ALTER TABLE %s%s ADD CONSTRAINT %s CHECK(%s)' %
-                           (SQL_PREFIX, rdef.subject.type, cstrname, check))
+                           (SQL_PREFIX, rdef.subject, cstrname, check))
 
 
 class CWUniqueTogetherConstraintAddOp(MemSchemaOperation):
--- a/cubicweb/schema.py	Fri Jul 08 10:17:14 2016 +0200
+++ b/cubicweb/schema.py	Fri Jul 08 10:17:42 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.
@@ -22,6 +22,7 @@
 
 import re
 from os.path import join, basename
+from hashlib import md5
 from logging import getLogger
 from warnings import warn
 
@@ -1145,6 +1146,16 @@
 
 # additional cw specific constraints ###########################################
 
+@monkeypatch(BaseConstraint)
+def name_for(self, rdef):
+    """Return a unique, size controlled, name for this constraint applied to given `rdef`.
+
+    This name may be used as name for the constraint in the database.
+    """
+    return 'cstr' + md5((rdef.subject.type + rdef.rtype.type + self.type() +
+                         (self.serialize() or '')).encode('ascii')).hexdigest()
+
+
 class BaseRQLConstraint(RRQLExpression, BaseConstraint):
     """base class for rql constraints"""
     distinct_query = None
--- a/cubicweb/server/schema2sql.py	Fri Jul 08 10:17:14 2016 +0200
+++ b/cubicweb/server/schema2sql.py	Fri Jul 08 10:17:42 2016 +0200
@@ -168,11 +168,8 @@
     """Return (constraint name, constraint SQL definition) for the given relation definition's
     constraint. Maybe (None, None) if the constraint is not handled in the backend.
     """
-    eschema = rdef.subject
     attr = rdef.rtype.type
-    # XXX should find a better name
-    cstrname = 'cstr' + md5((eschema.type + attr + constraint.type() +
-                             (constraint.serialize() or '')).encode('ascii')).hexdigest()
+    cstrname = constraint.name_for(rdef)
     if constraint.type() == 'BoundaryConstraint':
         value = constraint_value_as_sql(constraint.boundary, dbhelper, prefix)
         return cstrname, '%s%s %s %s' % (prefix, attr, constraint.operator, value)
--- a/cubicweb/server/test/unittest_migractions.py	Fri Jul 08 10:17:14 2016 +0200
+++ b/cubicweb/server/test/unittest_migractions.py	Fri Jul 08 10:17:42 2016 +0200
@@ -602,9 +602,7 @@
             self.assertEqual(len(constraints), 1, constraints)
             rdef = migrschema['promo'].rdefs['Personne', 'String']
             cstr = rdef.constraint_by_type('StaticVocabularyConstraint')
-            cstrname = 'cstr' + md5((rdef.subject.type + rdef.rtype.type + cstr.type() +
-                                     (cstr.serialize() or '')).encode('ascii')).hexdigest()
-            self.assertIn(cstrname, constraints)
+            self.assertIn(cstr.name_for(rdef), constraints)
 
     def _erqlexpr_rset(self, cnx, action, ertype):
         rql = 'RQLExpression X WHERE ET is CWEType, ET %s_permission X, ET name %%(name)s' % action