1 driver = config.system_source_config['db-driver'] |
|
2 if not (driver == 'postgres' or driver.startswith('sqlserver')): |
|
3 import sys |
|
4 print >>sys.stderr, 'This migration is not supported for backends other than sqlserver or postgres (yet).' |
|
5 sys.exit(1) |
|
6 |
|
7 add_relation_definition('CWAttribute', 'add_permission', 'CWGroup') |
|
8 add_relation_definition('CWAttribute', 'add_permission', 'RQLExpression') |
|
9 |
|
10 # a bad defaultval in 3.13.8 schema was fixed in 3.13.9, but the migration was missed |
|
11 rql('SET ATTR defaultval NULL WHERE ATTR from_entity E, E name "CWSource", ATTR relation_type T, T name "in_synchronization"') |
|
12 |
|
13 # the migration gets confused when we change rdefs out from under it. So |
|
14 # explicitly remove this size constraint so it doesn't stick around and break |
|
15 # things later. |
|
16 rdefeid = schema['defaultval'].rdefs.values()[0].eid |
|
17 rql('DELETE CWConstraint C WHERE C cstrtype T, T name "SizeConstraint", R constrained_by C, R eid %(eid)s', {'eid': rdefeid}) |
|
18 |
|
19 sync_schema_props_perms('defaultval') |
|
20 |
|
21 def convert_defaultval(cwattr, default): |
|
22 from decimal import Decimal |
|
23 import yams |
|
24 from cubicweb import Binary |
|
25 if default is None: |
|
26 return |
|
27 if isinstance(default, Binary): |
|
28 # partially migrated instance, try to be idempotent |
|
29 return default |
|
30 atype = cwattr.to_entity[0].name |
|
31 if atype == 'Boolean': |
|
32 # boolean attributes with default=False were stored as '' |
|
33 assert default in ('True', 'False', ''), repr(default) |
|
34 default = default == 'True' |
|
35 elif atype in ('Int', 'BigInt'): |
|
36 default = int(default) |
|
37 elif atype == 'Float': |
|
38 default = float(default) |
|
39 elif atype == 'Decimal': |
|
40 default = Decimal(default) |
|
41 elif atype in ('Date', 'Datetime', 'TZDatetime', 'Time'): |
|
42 try: |
|
43 # handle NOW and TODAY, keep them stored as strings |
|
44 yams.KEYWORD_MAP[atype][default.upper()] |
|
45 default = default.upper() |
|
46 except KeyError: |
|
47 # otherwise get an actual date or datetime |
|
48 default = yams.DATE_FACTORY_MAP[atype](default) |
|
49 else: |
|
50 assert atype == 'String', atype |
|
51 default = unicode(default) |
|
52 return Binary.zpickle(default) |
|
53 |
|
54 dbh = repo.system_source.dbhelper |
|
55 |
|
56 |
|
57 sql('ALTER TABLE cw_cwattribute ADD new_defaultval %s' % dbh.TYPE_MAPPING['Bytes']) |
|
58 |
|
59 for cwattr in rql('CWAttribute X').entities(): |
|
60 olddefault = cwattr.defaultval |
|
61 if olddefault is not None: |
|
62 req = "UPDATE cw_cwattribute SET new_defaultval = %(val)s WHERE cw_eid = %(eid)s" |
|
63 args = {'val': dbh.binary_value(convert_defaultval(cwattr, olddefault).getvalue()), 'eid': cwattr.eid} |
|
64 sql(req, args, ask_confirm=False) |
|
65 |
|
66 sql('ALTER TABLE cw_cwattribute DROP COLUMN cw_defaultval') |
|
67 if driver == 'postgres': |
|
68 sql('ALTER TABLE cw_cwattribute RENAME COLUMN new_defaultval TO cw_defaultval') |
|
69 else: # sqlserver |
|
70 sql("sp_rename 'cw_cwattribute.new_defaultval', 'cw_defaultval', 'COLUMN'") |
|
71 |
|
72 |
|
73 # Set object type to "Bytes" for CWAttribute's "defaultval" attribute |
|
74 rql('SET X to_entity B WHERE X is CWAttribute, X from_entity Y, Y name "CWAttribute", ' |
|
75 'X relation_type Z, Z name "defaultval", B name "Bytes", NOT X to_entity B') |
|
76 |
|
77 oldrdef = schema['CWAttribute'].rdef('defaultval') |
|
78 import yams.buildobjs as ybo |
|
79 newrdef = ybo.RelationDefinition('CWAttribute', 'defaultval', 'Bytes') |
|
80 newrdef.eid = oldrdef.eid |
|
81 schema.add_relation_def(newrdef) |
|
82 schema.del_relation_def('CWAttribute', 'defaultval', 'String') |
|
83 |
|
84 commit() |
|
85 |
|
86 sync_schema_props_perms('defaultval') |
|
87 |
|
88 for rschema in schema.relations(): |
|
89 if rschema.symmetric: |
|
90 subjects = set(repr(e.type) for e in rschema.subjects()) |
|
91 objects = set(repr(e.type) for e in rschema.objects()) |
|
92 assert subjects == objects |
|
93 martians = set(str(eid) for eid, in sql('SELECT eid_to FROM %s_relation, entities WHERE eid_to = eid AND type NOT IN (%s)' % |
|
94 (rschema.type, ','.join(subjects)))) |
|
95 martians |= set(str(eid) for eid, in sql('SELECT eid_from FROM %s_relation, entities WHERE eid_from = eid AND type NOT IN (%s)' % |
|
96 (rschema.type, ','.join(subjects)))) |
|
97 if martians: |
|
98 martians = ','.join(martians) |
|
99 print 'deleting broken relations %s for eids %s' % (rschema.type, martians) |
|
100 sql('DELETE FROM %s_relation WHERE eid_from IN (%s) OR eid_to IN (%s)' % (rschema.type, martians, martians)) |
|
101 with session.deny_all_hooks_but(): |
|
102 rql('SET X %(r)s Y WHERE Y %(r)s X, NOT X %(r)s Y' % {'r': rschema.type}) |
|
103 commit() |
|
104 |
|
105 |
|
106 # multi columns unique constraints regeneration |
|
107 from cubicweb.server import schemaserial |
|
108 |
|
109 # syncschema hooks would try to remove indices but |
|
110 # 1) we already do that below |
|
111 # 2) the hook expects the CWUniqueTogetherConstraint.name attribute that hasn't |
|
112 # yet been added |
|
113 with session.allow_all_hooks_but('syncschema'): |
|
114 rql('DELETE CWUniqueTogetherConstraint C') |
|
115 commit() |
|
116 |
|
117 add_attribute('CWUniqueTogetherConstraint', 'name') |
|
118 |
|
119 # low-level wipe code for postgres & sqlserver, plain sql ... |
|
120 if driver == 'postgres': |
|
121 for indexname, in sql('select indexname from pg_indexes'): |
|
122 if indexname.startswith('unique_'): |
|
123 print 'dropping index', indexname |
|
124 sql('DROP INDEX %s' % indexname) |
|
125 commit() |
|
126 elif driver.startswith('sqlserver'): |
|
127 for viewname, in sql('select name from sys.views'): |
|
128 if viewname.startswith('utv_'): |
|
129 print 'dropping view (index should be cascade-deleted)', viewname |
|
130 sql('DROP VIEW %s' % viewname) |
|
131 commit() |
|
132 |
|
133 # recreate the constraints, hook will lead to low-level recreation |
|
134 for eschema in sorted(schema.entities()): |
|
135 if eschema._unique_together: |
|
136 print 'recreate unique indexes for', eschema |
|
137 rql_args = schemaserial.uniquetogether2rqls(eschema) |
|
138 for rql, args in rql_args: |
|
139 args['x'] = eschema.eid |
|
140 session.execute(rql, args) |
|
141 commit() |
|
142 |
|
143 # all attributes perms have to be refreshed ... |
|
144 for rschema in sorted(schema.relations()): |
|
145 if rschema.final: |
|
146 if rschema.type in fsschema: |
|
147 print 'sync perms for', rschema.type |
|
148 sync_schema_props_perms(rschema.type, syncprops=False, ask_confirm=False, commit=False) |
|
149 else: |
|
150 print 'WARNING: attribute %s missing from fs schema' % rschema.type |
|
151 commit() |
|