server/migractions.py
branchtls-sprint
changeset 1398 5fe84a5f7035
parent 1263 01152fffd593
child 1399 3f408c7a164e
equal deleted inserted replaced
1397:6cbc7bc8ea6d 1398:5fe84a5f7035
    25 from logilab.common.adbh import get_adv_func_helper
    25 from logilab.common.adbh import get_adv_func_helper
    26 
    26 
    27 from yams.constraints import SizeConstraint
    27 from yams.constraints import SizeConstraint
    28 from yams.schema2sql import eschema2sql, rschema2sql
    28 from yams.schema2sql import eschema2sql, rschema2sql
    29 
    29 
    30 from cubicweb import AuthenticationError
    30 from cubicweb import AuthenticationError, ETYPE_NAME_MAP
    31 from cubicweb.dbapi import get_repository, repo_connect
    31 from cubicweb.dbapi import get_repository, repo_connect
    32 from cubicweb.common.migration import MigrationHelper, yes
    32 from cubicweb.common.migration import MigrationHelper, yes
    33 
    33 
    34 try:
    34 try:
    35     from cubicweb.server import schemaserial as ss
    35     from cubicweb.server import schemaserial as ss
    36     from cubicweb.server.utils import manager_userpasswd
    36     from cubicweb.server.utils import manager_userpasswd
    37     from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
    37     from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
    38 except ImportError: # LAX
    38 except ImportError: # LAX
    39     pass
    39     pass
    40 
       
    41 def set_sql_prefix(prefix):
       
    42     """3.1.5 migration function: allow to unset/reset SQL_PREFIX"""
       
    43     for module in ('checkintegrity', 'migractions', 'schemahooks',
       
    44                    'sources.rql2sql', 'sources.native'):
       
    45         try:
       
    46             sys.modules['cubicweb.server.%s' % module].SQL_PREFIX = prefix
       
    47             print 'changed SQL_PREFIX for %s' % module
       
    48         except KeyError:
       
    49             pass
       
    50         
       
    51 def update_database(repo):
       
    52     """3.1.3 migration function: update database schema by adding SQL_PREFIX to
       
    53     entity type tables and columns
       
    54     """
       
    55     pool = repo._get_pool()
       
    56     source = repo.system_source
       
    57     sqlcu = pool['system']
       
    58     for etype in repo.schema.entities():
       
    59         if etype.is_final():
       
    60             continue
       
    61         try:
       
    62             sqlcu.execute('ALTER TABLE %s RENAME TO cw_%s' % (etype, etype))
       
    63             print 'renamed %s table for source %s' % (etype, uri)
       
    64         except:
       
    65             pass
       
    66         for rschema in etype.subject_relations():
       
    67             if rschema == 'has_text':
       
    68                 continue
       
    69             if rschema.is_final() or rschema.inlined:
       
    70                 sqlcu.execute('ALTER TABLE cw_%s RENAME %s TO cw_%s'
       
    71                               % (etype, rschema, rschema))
       
    72                 print 'renamed %s.%s column for source %s' % (
       
    73                     etype, rschema, uri)
       
    74     pool.commit()
       
    75     repo._free_pool(pool)
       
    76 
    40 
    77         
    41         
    78 class ServerMigrationHelper(MigrationHelper):
    42 class ServerMigrationHelper(MigrationHelper):
    79     """specific migration helper for server side  migration scripts,
    43     """specific migration helper for server side  migration scripts,
    80     providind actions related to schema/data migration
    44     providind actions related to schema/data migration
    97         self.fs_schema = schema
    61         self.fs_schema = schema
    98         self._synchronized = set()
    62         self._synchronized = set()
    99 
    63 
   100     @cached
    64     @cached
   101     def repo_connect(self):
    65     def repo_connect(self):
   102         try:
    66         self.repo = get_repository(method='inmemory', config=self.config)
   103             self.repo = get_repository(method='inmemory', config=self.config)
       
   104         except:
       
   105             import traceback
       
   106             traceback.print_exc()
       
   107             print '3.1.5 migration'
       
   108             # XXX 3.1.5 migration
       
   109             set_sql_prefix('')
       
   110             self.repo = get_repository(method='inmemory', config=self.config)
       
   111             update_database(self.repo)
       
   112             set_sql_prefix('cw_')
       
   113         return self.repo
    67         return self.repo
   114     
    68     
   115     def shutdown(self):
    69     def shutdown(self):
   116         if self.repo is not None:
    70         if self.repo is not None:
   117             self.repo.shutdown()
    71             self.repo.shutdown()
   392                         self.cmd_drop_relation_definition(
   346                         self.cmd_drop_relation_definition(
   393                             str(fromtype), rschema.type, str(totype))
   347                             str(fromtype), rschema.type, str(totype))
   394         # execute post-remove files
   348         # execute post-remove files
   395         for pack in reversed(removedcubes):
   349         for pack in reversed(removedcubes):
   396             self.exec_event_script('postremove', self.config.cube_dir(pack))
   350             self.exec_event_script('postremove', self.config.cube_dir(pack))
   397             self.rqlexec('DELETE EProperty X WHERE X pkey %(pk)s',
   351             self.rqlexec('DELETE CWProperty X WHERE X pkey %(pk)s',
   398                          {'pk': u'system.version.'+pack}, ask_confirm=False)
   352                          {'pk': u'system.version.'+pack}, ask_confirm=False)
   399             self.commit()
   353             self.commit()
   400             
   354             
   401     # schema migration actions ################################################
   355     # schema migration actions ################################################
   402     
   356     
   447             if eschema.is_final():
   401             if eschema.is_final():
   448                 applschema.del_entity_type(etype)
   402                 applschema.del_entity_type(etype)
   449         else:
   403         else:
   450             eschema = self.fs_schema.eschema(etype)
   404             eschema = self.fs_schema.eschema(etype)
   451         confirm = self.verbosity >= 2
   405         confirm = self.verbosity >= 2
   452         # register the entity into EEType
   406         # register the entity into CWEType
   453         self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm)
   407         self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm)
   454         # add specializes relation if needed
   408         # add specializes relation if needed
   455         self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
   409         self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
   456         # register groups / permissions for the entity
   410         # register groups / permissions for the entity
   457         self.rqlexecall(ss.erperms2rql(eschema, self.group_mapping()),
   411         self.rqlexecall(ss.erperms2rql(eschema, self.group_mapping()),
   523         """unregister an existing entity type
   477         """unregister an existing entity type
   524         
   478         
   525         This will trigger deletion of necessary relation types and definitions
   479         This will trigger deletion of necessary relation types and definitions
   526         """
   480         """
   527         # XXX what if we delete an entity type which is specialized by other types
   481         # XXX what if we delete an entity type which is specialized by other types
   528         # unregister the entity from EEType
   482         # unregister the entity from CWEType
   529         self.rqlexec('DELETE EEType X WHERE X name %(etype)s', {'etype': etype},
   483         self.rqlexec('DELETE CWEType X WHERE X name %(etype)s', {'etype': etype},
   530                      ask_confirm=self.verbosity>=2)
   484                      ask_confirm=self.verbosity>=2)
   531         if commit:
   485         if commit:
   532             self.commit()
   486             self.commit()
   533 
   487 
   534     def cmd_rename_entity_type(self, oldname, newname, commit=True):
   488     def cmd_rename_entity_type(self, oldname, newname, commit=True):
   535         """rename an existing entity type in the persistent schema
   489         """rename an existing entity type in the persistent schema
   536         
   490         
   537         `oldname` is a string giving the name of the existing entity type
   491         `oldname` is a string giving the name of the existing entity type
   538         `newname` is a string giving the name of the renamed entity type
   492         `newname` is a string giving the name of the renamed entity type
   539         """
   493         """
   540         self.rqlexec('SET ET name %(newname)s WHERE ET is EEType, ET name %(oldname)s',
   494         self.rqlexec('SET ET name %(newname)s WHERE ET is CWEType, ET name %(oldname)s',
   541                      {'newname' : unicode(newname), 'oldname' : oldname})
   495                      {'newname' : unicode(newname), 'oldname' : oldname})
   542         if commit:
   496         if commit:
   543             self.commit()
   497             self.commit()
   544         
   498         
   545     def cmd_add_relation_type(self, rtype, addrdef=True, commit=True):
   499     def cmd_add_relation_type(self, rtype, addrdef=True, commit=True):
   552         creation (but not the relation definitions themselves, for which
   506         creation (but not the relation definitions themselves, for which
   553         committing depends on the `commit` argument value).
   507         committing depends on the `commit` argument value).
   554         
   508         
   555         """
   509         """
   556         rschema = self.fs_schema.rschema(rtype)
   510         rschema = self.fs_schema.rschema(rtype)
   557         # register the relation into ERType and insert necessary relation
   511         # register the relation into CWRType and insert necessary relation
   558         # definitions
   512         # definitions
   559         self.rqlexecall(ss.rschema2rql(rschema, addrdef=False),
   513         self.rqlexecall(ss.rschema2rql(rschema, addrdef=False),
   560                         ask_confirm=self.verbosity>=2)
   514                         ask_confirm=self.verbosity>=2)
   561         # register groups / permissions for the relation
   515         # register groups / permissions for the relation
   562         self.rqlexecall(ss.erperms2rql(rschema, self.group_mapping()),
   516         self.rqlexecall(ss.erperms2rql(rschema, self.group_mapping()),
   568         if commit:
   522         if commit:
   569             self.commit()
   523             self.commit()
   570         
   524         
   571     def cmd_drop_relation_type(self, rtype, commit=True):
   525     def cmd_drop_relation_type(self, rtype, commit=True):
   572         """unregister an existing relation type"""
   526         """unregister an existing relation type"""
   573         # unregister the relation from ERType
   527         # unregister the relation from CWRType
   574         self.rqlexec('DELETE ERType X WHERE X name %r' % rtype,
   528         self.rqlexec('DELETE CWRType X WHERE X name %r' % rtype,
   575                      ask_confirm=self.verbosity>=2)
   529                      ask_confirm=self.verbosity>=2)
   576         if commit:
   530         if commit:
   577             self.commit()
   531             self.commit()
   578         
   532         
   579     def cmd_rename_relation(self, oldname, newname, commit=True):
   533     def cmd_rename_relation(self, oldname, newname, commit=True):
   600             self.commit()
   554             self.commit()
   601         
   555         
   602     def cmd_drop_relation_definition(self, subjtype, rtype, objtype, commit=True):
   556     def cmd_drop_relation_definition(self, subjtype, rtype, objtype, commit=True):
   603         """unregister an existing relation definition"""
   557         """unregister an existing relation definition"""
   604         rschema = self.repo.schema.rschema(rtype)
   558         rschema = self.repo.schema.rschema(rtype)
   605         # unregister the definition from EFRDef or ENFRDef
   559         # unregister the definition from CWAttribute or CWRelation
   606         if rschema.is_final():
   560         if rschema.is_final():
   607             etype = 'EFRDef'
   561             etype = 'CWAttribute'
   608         else:
   562         else:
   609             etype = 'ENFRDef'
   563             etype = 'CWRelation'
   610         rql = ('DELETE %s X WHERE X from_entity FE, FE name "%s",'
   564         rql = ('DELETE %s X WHERE X from_entity FE, FE name "%s",'
   611                'X relation_type RT, RT name "%s", X to_entity TE, TE name "%s"')
   565                'X relation_type RT, RT name "%s", X to_entity TE, TE name "%s"')
   612         self.rqlexec(rql % (etype, subjtype, rtype, objtype),
   566         self.rqlexec(rql % (etype, subjtype, rtype, objtype),
   613                      ask_confirm=self.verbosity>=2)
   567                      ask_confirm=self.verbosity>=2)
   614         if commit:
   568         if commit:
   732         except KeyError:
   686         except KeyError:
   733             return
   687             return
   734         repospschema = repoeschema.specializes()
   688         repospschema = repoeschema.specializes()
   735         espschema = eschema.specializes()
   689         espschema = eschema.specializes()
   736         if repospschema and not espschema:
   690         if repospschema and not espschema:
   737             self.rqlexec('DELETE X specializes Y WHERE X is EEType, X name %(x)s',
   691             self.rqlexec('DELETE X specializes Y WHERE X is CWEType, X name %(x)s',
   738                          {'x': str(repoeschema)})
   692                          {'x': str(repoeschema)})
   739         elif not repospschema and espschema:
   693         elif not repospschema and espschema:
   740             self.rqlexec('SET X specializes Y WHERE X is EEType, X name %(x)s, '
   694             self.rqlexec('SET X specializes Y WHERE X is CWEType, X name %(x)s, '
   741                          'Y is EEType, Y name %(y)s',
   695                          'Y is CWEType, Y name %(y)s',
   742                          {'x': str(repoeschema), 'y': str(espschema)})
   696                          {'x': str(repoeschema), 'y': str(espschema)})
   743         self.rqlexecall(ss.updateeschema2rql(eschema),
   697         self.rqlexecall(ss.updateeschema2rql(eschema),
   744                         ask_confirm=self.verbosity >= 2)
   698                         ask_confirm=self.verbosity >= 2)
   745         for rschema, targettypes, x in eschema.relation_definitions(True):
   699         for rschema, targettypes, x in eschema.relation_definitions(True):
   746             if x == 'subject':
   700             if x == 'subject':
   798                 newcstr = None
   752                 newcstr = None
   799             if newcstr is None:
   753             if newcstr is None:
   800                 self.rqlexec('DELETE X constrained_by C WHERE C eid %(x)s',
   754                 self.rqlexec('DELETE X constrained_by C WHERE C eid %(x)s',
   801                              {'x': cstr.eid}, 'x',
   755                              {'x': cstr.eid}, 'x',
   802                              ask_confirm=confirm)
   756                              ask_confirm=confirm)
   803                 self.rqlexec('DELETE EConstraint C WHERE C eid %(x)s',
   757                 self.rqlexec('DELETE CWConstraint C WHERE C eid %(x)s',
   804                              {'x': cstr.eid}, 'x',
   758                              {'x': cstr.eid}, 'x',
   805                              ask_confirm=confirm)
   759                              ask_confirm=confirm)
   806             else:
   760             else:
   807                 newconstraints.remove(newcstr)
   761                 newconstraints.remove(newcstr)
   808                 values = {'x': cstr.eid,
   762                 values = {'x': cstr.eid,
   861             if isinstance(constr, SizeConstraint):
   815             if isinstance(constr, SizeConstraint):
   862                 oldvalue = constr.max
   816                 oldvalue = constr.max
   863         if oldvalue == size:
   817         if oldvalue == size:
   864             return
   818             return
   865         if oldvalue is None and not size is None:
   819         if oldvalue is None and not size is None:
   866             ceid = self.rqlexec('INSERT EConstraint C: C value %(v)s, C cstrtype CT '
   820             ceid = self.rqlexec('INSERT CWConstraint C: C value %(v)s, C cstrtype CT '
   867                                 'WHERE CT name "SizeConstraint"',
   821                                 'WHERE CT name "SizeConstraint"',
   868                                 {'v': SizeConstraint(size).serialize()},
   822                                 {'v': SizeConstraint(size).serialize()},
   869                                 ask_confirm=self.verbosity>=2)[0][0]
   823                                 ask_confirm=self.verbosity>=2)[0][0]
   870             self.rqlexec('SET X constrained_by C WHERE X from_entity S, X relation_type R, '
   824             self.rqlexec('SET X constrained_by C WHERE X from_entity S, X relation_type R, '
   871                          'S name "%s", R name "%s", C eid %s' % (etype, rtype, ceid),
   825                          'S name "%s", R name "%s", C eid %s' % (etype, rtype, ceid),
   881                 self.rqlexec('DELETE X constrained_by C WHERE X from_entity S, X relation_type R,'
   835                 self.rqlexec('DELETE X constrained_by C WHERE X from_entity S, X relation_type R,'
   882                              'X constrained_by C, C cstrtype CT, CT name "SizeConstraint",'
   836                              'X constrained_by C, C cstrtype CT, CT name "SizeConstraint",'
   883                              'S name "%s", R name "%s"' % (etype, rtype),
   837                              'S name "%s", R name "%s"' % (etype, rtype),
   884                              ask_confirm=self.verbosity>=2)
   838                              ask_confirm=self.verbosity>=2)
   885                 # cleanup unused constraints
   839                 # cleanup unused constraints
   886                 self.rqlexec('DELETE EConstraint C WHERE NOT X constrained_by C')
   840                 self.rqlexec('DELETE CWConstraint C WHERE NOT X constrained_by C')
   887         if commit:
   841         if commit:
   888             self.commit()
   842             self.commit()
   889     
   843     
   890     # Workflows handling ######################################################
   844     # Workflows handling ######################################################
   891     
   845     
   962         entity = self.session.eid_rset(eid).get_entity(0, 0)
   916         entity = self.session.eid_rset(eid).get_entity(0, 0)
   963         entity.change_state(entity.wf_state(statename).eid)
   917         entity.change_state(entity.wf_state(statename).eid)
   964         if commit:
   918         if commit:
   965             self.commit()
   919             self.commit()
   966         
   920         
   967     # EProperty handling ######################################################
   921     # CWProperty handling ######################################################
   968 
   922 
   969     def cmd_property_value(self, pkey):
   923     def cmd_property_value(self, pkey):
   970         rql = 'Any V WHERE X is EProperty, X pkey %(k)s, X value V'
   924         rql = 'Any V WHERE X is CWProperty, X pkey %(k)s, X value V'
   971         rset = self.rqlexec(rql, {'k': pkey}, ask_confirm=False)
   925         rset = self.rqlexec(rql, {'k': pkey}, ask_confirm=False)
   972         return rset[0][0]
   926         return rset[0][0]
   973 
   927 
   974     def cmd_set_property(self, pkey, value):
   928     def cmd_set_property(self, pkey, value):
   975         value = unicode(value)
   929         value = unicode(value)
   976         try:
   930         try:
   977             prop = self.rqlexec('EProperty X WHERE X pkey %(k)s', {'k': pkey},
   931             prop = self.rqlexec('CWProperty X WHERE X pkey %(k)s', {'k': pkey},
   978                                 ask_confirm=False).get_entity(0, 0)
   932                                 ask_confirm=False).get_entity(0, 0)
   979         except:
   933         except:
   980             self.cmd_add_entity('EProperty', pkey=unicode(pkey), value=value)
   934             self.cmd_add_entity('CWProperty', pkey=unicode(pkey), value=value)
   981         else:
   935         else:
   982             self.rqlexec('SET X value %(v)s WHERE X pkey %(k)s',
   936             self.rqlexec('SET X value %(v)s WHERE X pkey %(k)s',
   983                          {'k': pkey, 'v': value}, ask_confirm=False)
   937                          {'k': pkey, 'v': value}, ask_confirm=False)
   984 
   938 
   985     # other data migration commands ###########################################
   939     # other data migration commands ###########################################
  1062         * the actual schema won't be updated until next startup
  1016         * the actual schema won't be updated until next startup
  1063         """
  1017         """
  1064         rschema = self.repo.schema.rschema(attr)
  1018         rschema = self.repo.schema.rschema(attr)
  1065         oldtype = rschema.objects(etype)[0]
  1019         oldtype = rschema.objects(etype)[0]
  1066         rdefeid = rschema.rproperty(etype, oldtype, 'eid')
  1020         rdefeid = rschema.rproperty(etype, oldtype, 'eid')
  1067         sql = ("UPDATE EFRDef "
  1021         sql = ("UPDATE CWAttribute "
  1068                "SET to_entity=(SELECT eid FROM EEType WHERE name='%s')"
  1022                "SET to_entity=(SELECT eid FROM CWEType WHERE name='%s')"
  1069                "WHERE eid=%s") % (newtype, rdefeid)
  1023                "WHERE eid=%s") % (newtype, rdefeid)
  1070         self.sqlexec(sql, ask_confirm=False)
  1024         self.sqlexec(sql, ask_confirm=False)
  1071         dbhelper = self.repo.system_source.dbhelper
  1025         dbhelper = self.repo.system_source.dbhelper
  1072         sqltype = dbhelper.TYPE_MAPPING[newtype]
  1026         sqltype = dbhelper.TYPE_MAPPING[newtype]
  1073         sql = 'ALTER TABLE %s ALTER COLUMN %s TYPE %s' % (etype, attr, sqltype)
  1027         sql = 'ALTER TABLE %s ALTER COLUMN %s TYPE %s' % (etype, attr, sqltype)