[schema] use json to serialize constraints
Require yams 0.43: constraints are serialized to json, which means we
need to recreate the actual checks in the database on upgrade.
Temporary deps in tox.ini to pull respective changes in yams.
--- a/cubicweb.spec Wed Mar 16 17:59:10 2016 +0100
+++ b/cubicweb.spec Wed Mar 16 11:56:32 2016 +0100
@@ -25,7 +25,7 @@
Requires: %{python}-logilab-common >= 1.2.0
Requires: %{python}-logilab-mtconverter >= 0.8.0
Requires: %{python}-rql >= 0.34.0
-Requires: %{python}-yams >= 0.42.0
+Requires: %{python}-yams >= 0.43.0
Requires: %{python}-logilab-database >= 1.15.0
Requires: %{python}-passlib
Requires: %{python}-lxml
--- a/cubicweb/__pkginfo__.py Wed Mar 16 17:59:10 2016 +0100
+++ b/cubicweb/__pkginfo__.py Wed Mar 16 11:56:32 2016 +0100
@@ -43,7 +43,7 @@
'logilab-common': '>= 1.2.0',
'logilab-mtconverter': '>= 0.8.0',
'rql': '>= 0.34.0',
- 'yams': '>= 0.42.0',
+ 'yams': '>= 0.42.0', # TODO '>= 0.43.0' upon release.
#gettext # for xgettext, msgcat, etc...
# web dependencies
'lxml': '',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cubicweb/misc/migration/3.23.0_Any.py Wed Mar 16 11:56:32 2016 +0100
@@ -0,0 +1,24 @@
+# we changed constraint serialization, which also changes their name
+from cubicweb.server.schema2sql import check_constraint
+
+helper = repo.system_source.dbhelper
+
+for table, cstr in sql("""
+ SELECT table_name, constraint_name FROM information_schema.constraint_column_usage
+ WHERE constraint_name LIKE 'cstr%'"""):
+ sql("ALTER TABLE %(table)s DROP CONSTRAINT %(cstr)s" % locals())
+
+for cwconstraint in rql('Any C WHERE R constrained_by C').entities():
+ cwrdef = cwconstraint.reverse_constrained_by[0]
+ rdef = cwrdef.yams_schema()
+ cstr = rdef.constraint_by_eid(cwconstraint.eid)
+ if cstr.type() not in ('BoundaryConstraint', 'IntervalBoundConstraint',
+ 'StaticVocabularyConstraint'):
+ # These cannot be translate into backend CHECK.
+ continue
+ cstrname, check = check_constraint(rdef.subject, rdef.object, rdef.rtype.type,
+ cstr, helper, prefix='cw_')
+ args = {'e': rdef.subject.type, 'c': cstrname, 'v': check}
+ sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s CHECK(%(v)s)' % args)
+
+commit()
--- a/cubicweb/schema.py Wed Mar 16 17:59:10 2016 +0100
+++ b/cubicweb/schema.py Wed Mar 16 11:56:32 2016 +0100
@@ -40,7 +40,8 @@
from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \
RelationDefinitionSchema, PermissionMixIn, role_name
from yams.constraints import (BaseConstraint, FormatConstraint, BoundaryConstraint,
- IntervalBoundConstraint, StaticVocabularyConstraint)
+ IntervalBoundConstraint, StaticVocabularyConstraint,
+ cstr_json_dumps, cstr_json_loads)
from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader,
cleanup_sys_modules, fill_schema_from_namespace)
@@ -1145,13 +1146,17 @@
distinct_query = None
def serialize(self):
- # start with a semicolon for bw compat, see below
- return ';' + ','.join(sorted(self.mainvars)) + ';' + self.expression
+ return cstr_json_dumps({u'mainvars': sorted(self.mainvars),
+ u'expression': self.expression})
@classmethod
def deserialize(cls, value):
- _, mainvars, expression = value.split(';', 2)
- return cls(expression, mainvars)
+ try:
+ d = cstr_json_loads(value)
+ return cls(d['expression'], d['mainvars'])
+ except ValueError:
+ _, mainvars, expression = value.split(';', 2)
+ return cls(expression, mainvars)
def check(self, entity, rtype, value):
"""return true if the value satisfy the constraint, else false"""
@@ -1199,15 +1204,20 @@
self.msg = msg
def serialize(self):
- # start with a semicolon for bw compat, see below
- return ';%s;%s\n%s' % (','.join(sorted(self.mainvars)), self.expression,
- self.msg or '')
+ return cstr_json_dumps({
+ u'mainvars': sorted(self.mainvars),
+ u'expression': self.expression,
+ u'msg': self.msg})
@classmethod
def deserialize(cls, value):
- value, msg = value.split('\n', 1)
- _, mainvars, expression = value.split(';', 2)
- return cls(expression, mainvars, msg)
+ try:
+ d = cstr_json_loads(value)
+ return cls(d['expression'], d['mainvars'], d['msg'])
+ except ValueError:
+ value, msg = value.split('\n', 1)
+ _, mainvars, expression = value.split(';', 2)
+ return cls(expression, mainvars, msg)
def repo_check(self, session, eidfrom, rtype, eidto=None):
"""raise ValidationError if the relation doesn't satisfy the constraint
--- a/cubicweb/server/test/unittest_schema2sql.py Wed Mar 16 17:59:10 2016 +0100
+++ b/cubicweb/server/test/unittest_schema2sql.py Wed Mar 16 11:56:32 2016 +0100
@@ -52,7 +52,7 @@
d2 date,
t1 time,
t2 time
-, CONSTRAINT cstredd407706bdfbd2285714dd689e8fcc0 CHECK(d1 <= CAST(clock_timestamp() AS DATE))
+, CONSTRAINT cstr67c656afbcbfadd4be34d75656a2521a CHECK(d1 <= CAST(clock_timestamp() AS DATE))
);
CREATE TABLE Division(
@@ -97,7 +97,7 @@
datenaiss date,
test boolean,
salary float
-, CONSTRAINT cstr41fe7db9ce1d5be95de2477e26590386 CHECK(promo IN ('bon', 'pasbon'))
+, CONSTRAINT cstrdedefafc86dc831341c33547388c25bb CHECK(promo IN ('bon', 'pasbon'))
);
CREATE UNIQUE INDEX unique_e6c2d219772dbf1715597f7d9a6b3892 ON Person(nom,prenom);
@@ -115,7 +115,7 @@
datenaiss date,
test boolean,
salary float
-, CONSTRAINT cstrc8556fcc665865217761cdbcd220cae0 CHECK(promo IN ('bon', 'pasbon'))
+, CONSTRAINT cstrb62a1623de9e9b92eb552706b6ce0890 CHECK(promo IN ('bon', 'pasbon'))
);
CREATE UNIQUE INDEX unique_98da0f9de8588baa8966f0b1a6f850a3 ON Salaried(nom,prenom);
@@ -130,7 +130,7 @@
ad3 varchar(128),
cp varchar(12),
ville varchar(32)
-, CONSTRAINT cstrc51dd462e9f6115506a0fe468d4c8114 CHECK(fax <= tel)
+, CONSTRAINT cstraf91cb60287eec6d5c1175075edcccc0 CHECK(fax <= tel)
);
CREATE TABLE State(
@@ -159,8 +159,8 @@
author_email varchar(100) NOT NULL,
mailinglist varchar(100),
debian_handler varchar(6)
-, CONSTRAINT cstr70f766f834557c715815d76f0a0db956 CHECK(license IN ('GPL', 'ZPL'))
-, CONSTRAINT cstr831a117424d0007ae0278cc15f344f5e CHECK(debian_handler IN ('machin', 'bidule'))
+, CONSTRAINT cstree063a486aa92d4f721ced56c819f38a CHECK(license IN ('GPL', 'ZPL'))
+, CONSTRAINT cstrafb4b6de5d50b5a2eca60b33b7acf59b CHECK(debian_handler IN ('machin', 'bidule'))
);
--- a/cubicweb/server/test/unittest_schemaserial.py Wed Mar 16 17:59:10 2016 +0100
+++ b/cubicweb/server/test/unittest_schemaserial.py Wed Mar 16 11:56:32 2016 +0100
@@ -115,7 +115,7 @@
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X '
'WHERE CT eid %(ct)s, EDEF eid %(x)s',
{'x': None, 'ct': u'RQLConstraint_eid',
- 'value': u';O;O final TRUE\n'}),
+ 'value': u'{"expression": "O final TRUE", "mainvars": ["O"], "msg": null}'}),
('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,'
'X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,'
@@ -125,7 +125,7 @@
'ordernum': 1, 'cardinality': u'1*'}),
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X '
'WHERE CT eid %(ct)s, EDEF eid %(x)s',
- {'x': None, 'ct': u'RQLConstraint_eid', 'value': u';O;O final FALSE\n'}),
+ {'x': None, 'ct': u'RQLConstraint_eid', 'value': u'{"expression": "O final FALSE", "mainvars": ["O"], "msg": null}'}),
],
list(rschema2rql(schema.rschema('relation_type'), cstrtypemap)))
@@ -236,12 +236,12 @@
'WHERE CT eid %(ct)s, EDEF eid %(x)s',
{'x': None,
'ct': u'SizeConstraint_eid',
- 'value': u'max=2'}),
+ 'value': u'{"max": 2, "min": null}'}),
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X '
'WHERE CT eid %(ct)s, EDEF eid %(x)s',
{'x': None,
'ct': u'StaticVocabularyConstraint_eid',
- 'value': u"u'?1', u'11'"}),
+ 'value': u'["?1", "11"]'}),
('INSERT CWAttribute X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,'
'X description %(description)s,X formula %(formula)s,X fulltextindexed %(fulltextindexed)s,'
@@ -263,13 +263,13 @@
'WHERE CT eid %(ct)s, EDEF eid %(x)s',
{'x': None,
'ct': u'SizeConstraint_eid',
- 'value': u'max=2'}),
+ 'value': u'{"max": 2, "min": null}'}),
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X '
'WHERE CT eid %(ct)s, EDEF eid %(x)s',
{'x': None,
'ct': u'StaticVocabularyConstraint_eid',
- 'value': (u"u'?*', u'1*', u'+*', u'**', u'?+', u'1+', u'++', u'*+', u'?1', "
- "u'11', u'+1', u'*1', u'??', u'1?', u'+?', u'*?'")})],
+ "value": (u'["?*", "1*", "+*", "**", "?+", "1+", "++", "*+", "?1", '
+ u'"11", "+1", "*1", "??", "1?", "+?", "*?"]')})],
list(rschema2rql(schema.rschema('cardinality'), cstrtypemap)))
def test_rschema2rql_custom_type(self):
@@ -334,7 +334,7 @@
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X '
'WHERE CT eid %(ct)s, EDEF eid %(x)s',
{'x': None,
- 'value': u'max=50',
+ 'value': u'{"max": 50, "min": null}',
'ct': 'SizeConstraint_eid'})],
list(rdef2rql(schema['description_format'].rdefs[('CWRType', 'String')],
cstrtypemap)))
--- a/debian/control Wed Mar 16 17:59:10 2016 +0100
+++ b/debian/control Wed Mar 16 11:56:32 2016 +0100
@@ -16,7 +16,7 @@
python-markdown,
python-tz,
python-rql (>= 0.34.0),
- python-yams (>= 0.42.0),
+ python-yams (>= 0.43.0),
python-lxml,
Standards-Version: 3.9.1
Homepage: https://www.cubicweb.org
@@ -160,7 +160,7 @@
python-logilab-mtconverter (>= 0.8.0),
python-logilab-common (>= 1.2.0),
python-markdown,
- python-yams (>= 0.42.0),
+ python-yams (>= 0.43.0),
python-rql (>= 0.34.0),
python-lxml
Recommends:
--- a/tox.ini Wed Mar 16 17:59:10 2016 +0100
+++ b/tox.ini Wed Mar 16 11:56:32 2016 +0100
@@ -9,6 +9,7 @@
whitelist_externals =
/usr/bin/touch
deps =
+ hg+https://hg.logilab.org/review/yams@default#egg=yams
py34: -e.
cubicweb: -r{toxinidir}/cubicweb/test/requirements.txt
devtools: -r{toxinidir}/cubicweb/devtools/test/requirements.txt