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) |