fix #327301: synchronize_schema doesn't update not-null constraint stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 28 May 2009 11:41:36 +0200
branchstable
changeset 1981 e6eed4324357
parent 1975 cb9686969ef7
child 1990 59507a12a7f4
child 1992 b073057c2756
fix #327301: synchronize_schema doesn't update not-null constraint
server/schemahooks.py
server/test/unittest_hooks.py
--- a/server/schemahooks.py	Wed May 27 18:51:52 2009 +0200
+++ b/server/schemahooks.py	Thu May 28 11:41:36 2009 +0200
@@ -6,8 +6,9 @@
 checking for schema consistency is done in hooks.py
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -187,7 +188,7 @@
     DeleteCWRTypeOp(session, name)
 
 
-class DelErdefOp(SchemaOperation):
+class DelRelationDefOp(SchemaOperation):
     """actually remove the relation definition from the application's schema"""
     def commit_event(self):
         subjtype, rtype, objtype = self.kobj
@@ -236,7 +237,7 @@
     # if this is the last instance, drop associated relation type
     if lastrel and not rteid in pendings:
         execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x')
-    DelErdefOp(session, (subjschema, rschema, objschema))
+    DelRelationDefOp(session, (subjschema, rschema, objschema))
 
 
 # addition ####################################################################
@@ -561,7 +562,7 @@
         self.session.repo.schema.rename_entity_type(self.oldname, self.newname)
 
 
-class UpdateRdefOp(SchemaOperation):
+class UpdateRelationDefOp(SchemaOperation):
     """actually update some properties of a relation definition"""
     rschema = values = None # make pylint happy
 
@@ -575,6 +576,19 @@
                 sysource.create_index(self.session, table, column)
             else:
                 sysource.drop_index(self.session, table, column)
+        if 'cardinality' in self.values and self.rschema.is_final():
+            if self.session.pool.source('system').dbdriver == 'sqlite':
+                # not supported (and NOT NULL not set by yams in that case, so
+                # no worry)
+                return
+            sqlexec = self.session.system_sql
+            etype, rtype = self.kobj[0], self.rschema.type
+            if self.values['cardinality'][0] == '1':
+                cmd = 'SET'
+            else:
+                cmd = 'DROP'
+            sqlexec('ALTER TABLE %s ALTER COLUMN %s %s NOT NULL' % (
+                table, SQL_PREFIX + etype, SQL_PREFIX + rtype))
 
     def commit_event(self):
         # structure should be clean, not need to remove entity's relations
@@ -595,8 +609,8 @@
             newvalues[prop] = entity[prop]
     if newvalues:
         subjtype = entity.from_entity[0].name
-        UpdateRdefOp(session, (subjtype, desttype), rschema=rschema,
-                     values=newvalues)
+        UpdateRelationDefOp(session, (subjtype, desttype),
+                            rschema=rschema, values=newvalues)
 
 
 class UpdateRtypeOp(SchemaOperation):
--- a/server/test/unittest_hooks.py	Wed May 27 18:51:52 2009 +0200
+++ b/server/test/unittest_hooks.py	Thu May 28 11:41:36 2009 +0200
@@ -2,6 +2,7 @@
 """functional tests for core hooks
 
 note: most schemahooks.py hooks are actually tested in unittest_migrations.py
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from logilab.common.testlib import TestCase, unittest_main
@@ -454,6 +455,23 @@
             self.failIf(self.schema['Affaire'].has_unique_values('sujet'))
             self.failIf(self.index_exists('Affaire', 'sujet', unique=True))
 
+    def test_required_change_1(self):
+        self.execute('SET DEF cardinality "?1" '
+                     'WHERE DEF relation_type RT, DEF from_entity E,'
+                     'RT name "nom", E name "Personne"')
+        self.commit()
+        # should now be able to add personne without nom
+        self.execute('INSERT Personne X')
+        self.commit()
+
+    def test_required_change_2(self):
+        self.execute('SET DEF cardinality "11" '
+                     'WHERE DEF relation_type RT, DEF from_entity E,'
+                     'RT name "prenom", E name "Personne"')
+        self.commit()
+        # should not be able anymore to add personne without prenom
+        self.assertRaises(ValidationError, self.execute, 'INSERT Personne X: X nom "toto"')
+
 
 class WorkflowHooksTC(RepositoryBasedTC):