cubicweb/misc/migration/3.21.0_Any.py
author Denis Laxalde <denis.laxalde@logilab.fr>
Sat, 16 Jan 2016 13:48:51 +0100
changeset 11057 0b59724cb3f2
parent 10651 misc/migration/3.21.0_Any.py@9ca33768473c
child 11129 97095348b3ee
permissions -rw-r--r--
Reorganize source tree to have a "cubicweb" top-level package Basically: mkdir cubicweb hg mv *.py -X setup.py cubicweb hg mv dataimport devtools entities etwist ext hooks i18n misc schemas server skeleton sobjects test web wsgi cubicweb Other changes: * adjust path to cubicweb-ctl in devtools tests * update setup.py to avoid importing __pkginfo__ (exec it instead), replace os.path.walk by os.walk and prepend `modname` here and there * update tox.ini to account for new test locations * update doc/conf.py so that it still finds __pkginfo__.py and CWDIR in doc/Makefile

from __future__ import print_function

from cubicweb.schema import PURE_VIRTUAL_RTYPES
from cubicweb.server.schema2sql import rschema_has_table


def add_foreign_keys():
    source = repo.system_source
    if not source.dbhelper.alter_column_support:
        return
    for rschema in schema.relations():
        if rschema.inlined:
            add_foreign_keys_inlined(rschema)
        elif rschema_has_table(rschema, skip_relations=PURE_VIRTUAL_RTYPES):
            add_foreign_keys_relation(rschema)
    for eschema in schema.entities():
        if eschema.final:
            continue
        add_foreign_key_etype(eschema)


def add_foreign_keys_relation(rschema):
    args = {'r': rschema.type}
    count = sql('SELECT COUNT(*) FROM ('
                '    SELECT eid_from FROM %(r)s_relation'
                '  UNION'
                '    SELECT eid_to FROM %(r)s_relation'
                '  EXCEPT'
                '    SELECT eid FROM entities) AS eids' % args,
                ask_confirm=False)[0][0]
    if count:
        print('%s references %d unknown entities, deleting' % (rschema, count))
        sql('DELETE FROM %(r)s_relation '
            'WHERE eid_from IN (SELECT eid_from FROM %(r)s_relation EXCEPT SELECT eid FROM entities)' % args)
        sql('DELETE FROM %(r)s_relation '
            'WHERE eid_to IN (SELECT eid_to FROM %(r)s_relation EXCEPT SELECT eid FROM entities)' % args)

    args['from_fk'] = '%(r)s_relation_eid_from_fkey' % args
    args['to_fk'] = '%(r)s_relation_eid_to_fkey' % args
    args['table'] = '%(r)s_relation' % args
    if repo.system_source.dbdriver == 'postgres':
        sql('ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(from_fk)s' % args,
            ask_confirm=False)
        sql('ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(to_fk)s' % args,
            ask_confirm=False)
    elif repo.system_source.dbdriver.startswith('sqlserver'):
        sql("IF OBJECT_ID('%(from_fk)s', 'F') IS NOT NULL "
            "ALTER TABLE %(table)s DROP CONSTRAINT %(from_fk)s" % args,
            ask_confirm=False)
        sql("IF OBJECT_ID('%(to_fk)s', 'F') IS NOT NULL "
            "ALTER TABLE %(table)s DROP CONSTRAINT %(to_fk)s" % args,
            ask_confirm=False)
    sql('ALTER TABLE %(table)s ADD CONSTRAINT %(from_fk)s '
        'FOREIGN KEY (eid_from) REFERENCES entities (eid)' % args,
        ask_confirm=False)
    sql('ALTER TABLE %(table)s ADD CONSTRAINT %(to_fk)s '
        'FOREIGN KEY (eid_to) REFERENCES entities (eid)' % args,
        ask_confirm=False)


def add_foreign_keys_inlined(rschema):
    for eschema in rschema.subjects():
        args = {'e': eschema.type, 'r': rschema.type}
        args['c'] = 'cw_%(e)s_cw_%(r)s_fkey' % args

        if eschema.rdef(rschema).cardinality[0] == '1':
            broken_eids = sql('SELECT cw_eid FROM cw_%(e)s WHERE cw_%(r)s IS NULL' % args,
                              ask_confirm=False)
            if broken_eids:
                print('Required relation %(e)s.%(r)s missing' % args)
                args['eids'] = ', '.join(str(eid) for eid, in broken_eids)
                rql('DELETE %(e)s X WHERE X eid IN (%(eids)s)' % args)
            broken_eids = sql('SELECT cw_eid FROM cw_%(e)s WHERE cw_%(r)s IN (SELECT cw_%(r)s FROM cw_%(e)s '
                              'EXCEPT SELECT eid FROM entities)' % args,
                              ask_confirm=False)
            if broken_eids:
                print('Required relation %(e)s.%(r)s references unknown objects, deleting subject entities' % args)
                args['eids'] = ', '.join(str(eid) for eid, in broken_eids)
                rql('DELETE %(e)s X WHERE X eid IN (%(eids)s)' % args)
        else:
            if sql('SELECT COUNT(*) FROM ('
                   '    SELECT cw_%(r)s FROM cw_%(e)s WHERE cw_%(r)s IS NOT NULL'
                   '  EXCEPT'
                   '    SELECT eid FROM entities) AS eids' % args,
                   ask_confirm=False)[0][0]:
                print('%(e)s.%(r)s references unknown entities, deleting relation' % args)
                sql('UPDATE cw_%(e)s SET cw_%(r)s = NULL WHERE cw_%(r)s IS NOT NULL AND cw_%(r)s IN '
                    '(SELECT cw_%(r)s FROM cw_%(e)s EXCEPT SELECT eid FROM entities)' % args)

        if repo.system_source.dbdriver == 'postgres':
            sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args,
                ask_confirm=False)
        elif repo.system_source.dbdriver.startswith('sqlserver'):
            sql("IF OBJECT_ID('%(c)s', 'F') IS NOT NULL "
                "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args,
                ask_confirm=False)
        sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s '
            'FOREIGN KEY (cw_%(r)s) references entities(eid)' % args,
            ask_confirm=False)


def add_foreign_key_etype(eschema):
    args = {'e': eschema.type}
    if sql('SELECT COUNT(*) FROM ('
           '    SELECT cw_eid FROM cw_%(e)s'
           '  EXCEPT'
           '    SELECT eid FROM entities) AS eids' % args,
           ask_confirm=False)[0][0]:
        print('%(e)s has nonexistent entities, deleting' % args)
        sql('DELETE FROM cw_%(e)s WHERE cw_eid IN '
            '(SELECT cw_eid FROM cw_%(e)s EXCEPT SELECT eid FROM entities)' % args)
    args['c'] = 'cw_%(e)s_cw_eid_fkey' % args
    if repo.system_source.dbdriver == 'postgres':
        sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args,
            ask_confirm=False)
    elif repo.system_source.dbdriver.startswith('sqlserver'):
        sql("IF OBJECT_ID('%(c)s', 'F') IS NOT NULL "
            "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args,
            ask_confirm=False)
    sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s '
        'FOREIGN KEY (cw_eid) REFERENCES entities (eid)' % args,
        ask_confirm=False)


add_foreign_keys()

cu = session.cnxset.cu
helper = repo.system_source.dbhelper

helper.drop_index(cu, 'entities', 'extid', False)
# don't use create_index because it doesn't work for columns that may be NULL
# on sqlserver
for query in helper.sqls_create_multicol_unique_index('entities', ['extid']):
    cu.execute(query)

if 'moved_entities' not in helper.list_tables(cu):
    sql('''
    CREATE TABLE moved_entities (
      eid INTEGER PRIMARY KEY NOT NULL,
      extid VARCHAR(256) UNIQUE
    )
    ''')

moved_entities = sql('SELECT -eid, extid FROM entities WHERE eid < 0',
                     ask_confirm=False)
if moved_entities:
    cu.executemany('INSERT INTO moved_entities (eid, extid) VALUES (%s, %s)',
                   moved_entities)
    sql('DELETE FROM entities WHERE eid < 0')

commit()

sync_schema_props_perms('CWEType')

sync_schema_props_perms('cwuri')

from cubicweb.server.schema2sql import check_constraint

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'):
        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}
    if repo.system_source.dbdriver == 'postgres':
        sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args)
    elif repo.system_source.dbdriver.startswith('sqlserver'):
        sql("IF OBJECT_ID('%(c)s', 'C') IS NOT NULL "
            "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args)
    sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s CHECK(%(v)s)' % args)
commit()