misc/migration/3.18.0_Any.py
author Julien Cristau <julien.cristau@logilab.fr>
Thu, 12 Dec 2013 14:25:24 +0100
changeset 9360 eda5071e30a1
parent 9300 5f10cd13224d
child 9361 0542a85fe667
permissions -rw-r--r--
[migration] fix handling of default value for boolean attributes We can't assert that the old value is 'True' or 'False', because False used to be stored as an empty string in pre-3.18 versions.

sync_schema_props_perms('defaultval')

def convert_defaultval(cwattr, default):
    from decimal import Decimal
    import yams
    from cubicweb import Binary
    if default is None:
        return
    atype = cwattr.to_entity[0].name
    if atype == 'Boolean':
        # boolean attributes with default=False were stored as ''
        assert default in ('True', 'False', ''), repr(default)
        default = default == 'True'
    elif atype in ('Int', 'BigInt'):
        default = int(default)
    elif atype == 'Float':
        default = float(default)
    elif atype == 'Decimal':
        default = Decimal(default)
    elif atype in ('Date', 'Datetime', 'TZDatetime', 'Time'):
        try:
            # handle NOW and TODAY, keep them stored as strings
            yams.KEYWORD_MAP[atype][default.upper()]
            default = default.upper()
        except KeyError:
            # otherwise get an actual date or datetime
            default = yams.DATE_FACTORY_MAP[atype](default)
    else:
        assert atype == 'String', atype
        default = unicode(default)
    return Binary.zpickle(default)

dbh = repo.system_source.dbhelper
driver = config.sources()['system']['db-driver']

if driver == 'postgres' or driver.startswith('sqlserver'):

    sql('ALTER TABLE cw_cwattribute ADD new_defaultval %s' % dbh.TYPE_MAPPING['Bytes'])

    for cwattr in rql('CWAttribute X').entities():
        olddefault = cwattr.defaultval
        if olddefault is not None:
            req = "UPDATE cw_cwattribute SET new_defaultval = %(val)s WHERE cw_eid = %(eid)s"
            args = {'val': dbh.binary_value(convert_defaultval(cwattr, olddefault).getvalue()), 'eid': cwattr.eid}
            sql(req, args, ask_confirm=False)

    sql('ALTER TABLE cw_cwattribute DROP COLUMN cw_defaultval')
    if config.sources()['system']['db-driver'] == 'postgres':
        sql('ALTER TABLE cw_cwattribute RENAME COLUMN new_defaultval TO cw_defaultval')
    else:
        sql("sp_rename 'cw_cwattribute.new_defaultval', 'cw_defaultval', 'COLUMN'")

elif driver == 'sqlite':

    import re
    create = sql("SELECT sql FROM sqlite_master WHERE name = 'cw_CWAttribute'")[0][0]
    create = re.sub('cw_defaultval varchar[^,]*,', 'cw_defaultval bytea,', create, re.I)
    create = re.sub('cw_CWAttribute', 'tmp_cw_CWAttribute', create, re.I)
    sql(create)
    sql("INSERT INTO tmp_cw_CWAttribute SELECT * FROM cw_CWAttribute")
    for cwattr in rql('CWAttribute X').entities():
        olddefault = cwattr.defaultval
        if olddefault is None:
            continue
        req = "UPDATE tmp_cw_CWAttribute SET cw_defaultval = %(val)s WHERE cw_eid = %(eid)s"
        args = {'val': dbh.binary_value(convert_defaultval(cwattr, olddefault).getvalue()),
                'eid': cwattr.eid}
        sql(req, args, ask_confirm=False)

    sql('DROP TABLE cw_CWAttribute')
    sql('ALTER TABLE tmp_cw_CWAttribute RENAME TO cw_CWAttribute')

else:
    assert False, 'upgrade not supported on this database backend'

# Set object type to "Bytes" for CWAttribute's "defaultval" attribute
rql('SET X to_entity B WHERE X is CWAttribute, X from_entity Y, Y name "CWAttribute", '
    'X relation_type Z, Z name "defaultval", B name "Bytes"')

from yams import buildobjs as ybo
schema.add_relation_def(ybo.RelationDefinition('CWAttribute', 'defaultval', 'Bytes'))
schema.del_relation_def('CWAttribute', 'defaultval', 'String')

commit()