misc/migration/3.18.0_Any.py
changeset 10301 729f36a1bcfa
parent 10276 ffb269e60348
parent 10298 e52efb73f9ee
child 10302 7725396eb3df
equal deleted inserted replaced
10276:ffb269e60348 10301:729f36a1bcfa
     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()