10 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
10 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
11 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
11 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
12 """ |
12 """ |
13 __docformat__ = "restructuredtext en" |
13 __docformat__ = "restructuredtext en" |
14 |
14 |
15 from yams.schema import BASE_TYPES |
15 from yams.schema import BASE_TYPES, RelationSchema |
16 from yams.buildobjs import EntityType, RelationType, RelationDefinition |
16 from yams.buildobjs import EntityType, RelationType, RelationDefinition |
17 from yams.schema2sql import eschema2sql, rschema2sql, type_from_constraints |
17 from yams.schema2sql import eschema2sql, rschema2sql, type_from_constraints |
18 |
18 |
19 from logilab.common.decorators import clear_cache |
19 from logilab.common.decorators import clear_cache |
20 |
20 |
481 # when the relation is added in the same transaction, the constraint |
481 # when the relation is added in the same transaction, the constraint |
482 # object is created by the operation adding the attribute or relation, |
482 # object is created by the operation adding the attribute or relation, |
483 # so there is nothing to do here |
483 # so there is nothing to do here |
484 if session.added_in_transaction(rdef.eid): |
484 if session.added_in_transaction(rdef.eid): |
485 return |
485 return |
486 subjtype, rtype, objtype = session.vreg.schema.schema_by_eid(rdef.eid) |
486 rdefschema = session.vreg.schema.schema_by_eid(rdef.eid) |
|
487 subjtype, rtype, objtype = rdefschema.as_triple() |
487 cstrtype = self.entity.type |
488 cstrtype = self.entity.type |
488 oldcstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) |
489 oldcstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) |
489 newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value) |
490 newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value) |
490 table = SQL_PREFIX + str(subjtype) |
491 table = SQL_PREFIX + str(subjtype) |
491 column = SQL_PREFIX + str(rtype) |
492 column = SQL_PREFIX + str(rtype) |
601 rschema = values = None # make pylint happy |
602 rschema = values = None # make pylint happy |
602 |
603 |
603 def commit_event(self): |
604 def commit_event(self): |
604 # structure should be clean, not need to remove entity's relations |
605 # structure should be clean, not need to remove entity's relations |
605 # at this point |
606 # at this point |
606 self.rschema.rdef[self.kobj].update(self.values) |
607 self.rschema.rdefs[self.kobj].update(self.values) |
607 |
608 |
608 |
609 |
609 class MemSchemaRDefDel(MemSchemaOperation): |
610 class MemSchemaRDefDel(MemSchemaOperation): |
610 """actually remove the relation definition from the instance's schema""" |
611 """actually remove the relation definition from the instance's schema""" |
611 def commit_event(self): |
612 def commit_event(self): |
630 # object is created by the operation adding the attribute or relation, |
631 # object is created by the operation adding the attribute or relation, |
631 # so there is nothing to do here |
632 # so there is nothing to do here |
632 if self.session.added_in_transaction(rdef.eid): |
633 if self.session.added_in_transaction(rdef.eid): |
633 self.cancelled = True |
634 self.cancelled = True |
634 return |
635 return |
635 subjtype, rtype, objtype = self.session.vreg.schema.schema_by_eid(rdef.eid) |
636 rdef = self.session.vreg.schema.schema_by_eid(rdef.eid) |
|
637 subjtype, rtype, objtype = rdef.as_triple() |
636 self.prepare_constraints(subjtype, rtype, objtype) |
638 self.prepare_constraints(subjtype, rtype, objtype) |
637 cstrtype = self.entity.type |
639 cstrtype = self.entity.type |
638 self.cstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) |
640 self.cstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) |
639 self.newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value) |
641 self.newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value) |
640 self.newcstr.eid = self.entity.eid |
642 self.newcstr.eid = self.entity.eid |
666 """ |
668 """ |
667 |
669 |
668 def commit_event(self): |
670 def commit_event(self): |
669 """the observed connections pool has been commited""" |
671 """the observed connections pool has been commited""" |
670 try: |
672 try: |
671 erschema = self.session.vreg.schema[self.name] |
673 erschema = self.session.vreg.schema.schema_by_eid(self.eid) |
672 except KeyError: |
674 except KeyError: |
673 # duh, schema not found, log error and skip operation |
675 # duh, schema not found, log error and skip operation |
674 self.error('no schema for %s', self.name) |
676 self.error('no schema for %s', self.eid) |
675 return |
677 return |
676 perms = list(erschema.action_permissions(self.action)) |
678 perms = list(erschema.action_permissions(self.action)) |
677 if hasattr(self, group_eid): |
679 if hasattr(self, 'group_eid'): |
678 perm = self.session.entity_from_eid(self.group_eid).name |
680 perm = self.session.entity_from_eid(self.group_eid).name |
679 else: |
681 else: |
680 perm = erschema.rql_expression(self.expr) |
682 perm = erschema.rql_expression(self.expr) |
681 try: |
683 try: |
682 perms.index(perm) |
684 perms.index(perm) |
693 """ |
695 """ |
694 |
696 |
695 def commit_event(self): |
697 def commit_event(self): |
696 """the observed connections pool has been commited""" |
698 """the observed connections pool has been commited""" |
697 try: |
699 try: |
698 erschema = self.session.vreg.schema[self.name] |
700 erschema = self.session.vreg.schema.schema_by_eid(self.eid) |
699 except KeyError: |
701 except KeyError: |
700 # duh, schema not found, log error and skip operation |
702 # duh, schema not found, log error and skip operation |
701 self.error('no schema for %s', self.name) |
703 self.error('no schema for %s', self.eid) |
|
704 return |
|
705 if isinstance(erschema, RelationSchema): # XXX 3.6 migration |
702 return |
706 return |
703 perms = list(erschema.action_permissions(self.action)) |
707 perms = list(erschema.action_permissions(self.action)) |
704 if hasattr(self, group_eid): |
708 if hasattr(self, 'group_eid'): |
705 perm = self.session.entity_from_eid(self.group_eid).name |
709 perm = self.session.entity_from_eid(self.group_eid).name |
706 else: |
710 else: |
707 perm = erschema.rql_expression(self.expr) |
711 perm = erschema.rql_expression(self.expr) |
708 try: |
712 try: |
709 perms.remove(self.group) |
713 perms.remove(perm) |
710 erschema.set_action_permissions(self.action, perms) |
714 erschema.set_action_permissions(self.action, perms) |
711 except ValueError: |
715 except ValueError: |
712 self.error('can\'t remove permission %s for %s on %s', |
716 self.error('can\'t remove permission %s for %s on %s', |
713 perm, self.action, erschema) |
717 perm, self.action, erschema) |
714 |
718 |
914 def check_valid_changes(session, entity, ro_attrs=('name', 'final')): |
918 def check_valid_changes(session, entity, ro_attrs=('name', 'final')): |
915 errors = {} |
919 errors = {} |
916 # don't use getattr(entity, attr), we would get the modified value if any |
920 # don't use getattr(entity, attr), we would get the modified value if any |
917 for attr in ro_attrs: |
921 for attr in ro_attrs: |
918 if attr in entity.edited_attributes: |
922 if attr in entity.edited_attributes: |
919 origval, newval = entity_oldnewvalue(entity, attr) |
923 origval, newval = hook.entity_oldnewvalue(entity, attr) |
920 if newval != origval: |
924 if newval != origval: |
921 errors[attr] = session._("can't change the %s attribute") % \ |
925 errors[attr] = session._("can't change the %s attribute") % \ |
922 display_name(session, attr) |
926 display_name(session, attr) |
923 if errors: |
927 if errors: |
924 raise ValidationError(entity.eid, errors) |
928 raise ValidationError(entity.eid, errors) |
938 __select__ = SyncSchemaHook.__select__ & hook.match_rtype('relation_type') |
942 __select__ = SyncSchemaHook.__select__ & hook.match_rtype('relation_type') |
939 events = ('after_delete_relation',) |
943 events = ('after_delete_relation',) |
940 |
944 |
941 def __call__(self): |
945 def __call__(self): |
942 session = self._cw |
946 session = self._cw |
943 subjschema, rschema, objschema = session.vreg.schema.schema_by_eid(self.eidfrom) |
947 rdef = session.vreg.schema.schema_by_eid(self.eidfrom) |
944 subjschema, rschema, objschema = session.schema.schema_by_eid(rdefeid) |
948 subjschema, rschema, objschema = rdef.as_triple() |
945 pendings = session.transaction_data.get('pendingeids', ()) |
949 pendings = session.transaction_data.get('pendingeids', ()) |
946 pendingrdefs = session.transaction_data.setdefault('pendingrdefs', set()) |
950 pendingrdefs = session.transaction_data.setdefault('pendingrdefs', set()) |
947 # first delete existing relation if necessary |
951 # first delete existing relation if necessary |
948 if rschema.final: |
952 if rschema.final: |
949 rdeftype = 'CWAttribute' |
953 rdeftype = 'CWAttribute' |
954 if not (subjschema.eid in pendings or objschema.eid in pendings): |
958 if not (subjschema.eid in pendings or objschema.eid in pendings): |
955 session.execute('DELETE X %s Y WHERE X is %s, Y is %s' |
959 session.execute('DELETE X %s Y WHERE X is %s, Y is %s' |
956 % (rschema, subjschema, objschema)) |
960 % (rschema, subjschema, objschema)) |
957 execute = session.unsafe_execute |
961 execute = session.unsafe_execute |
958 rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R,' |
962 rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R,' |
959 'R eid %%(x)s' % rdeftype, {'x': rteid}) |
963 'R eid %%(x)s' % rdeftype, {'x': self.eidto}) |
960 lastrel = rset[0][0] == 0 |
964 lastrel = rset[0][0] == 0 |
961 # we have to update physical schema systematically for final and inlined |
965 # we have to update physical schema systematically for final and inlined |
962 # relations, but only if it's the last instance for this relation type |
966 # relations, but only if it's the last instance for this relation type |
963 # for other relations |
967 # for other relations |
964 |
968 |
965 if (rschema.final or rschema.inlined): |
969 if (rschema.final or rschema.inlined): |
966 rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R, ' |
970 rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R, ' |
967 'R eid %%(x)s, X from_entity E, E name %%(name)s' |
971 'R eid %%(x)s, X from_entity E, E name %%(name)s' |
968 % rdeftype, {'x': rteid, 'name': str(subjschema)}) |
972 % rdeftype, {'x': self.eidto, 'name': str(subjschema)}) |
969 if rset[0][0] == 0 and not subjschema.eid in pendings: |
973 if rset[0][0] == 0 and not subjschema.eid in pendings: |
970 ptypes = session.transaction_data.setdefault('pendingrtypes', set()) |
974 ptypes = session.transaction_data.setdefault('pendingrtypes', set()) |
971 ptypes.add(rschema.type) |
975 ptypes.add(rschema.type) |
972 DropColumn(session, table=SQL_PREFIX + subjschema.type, |
976 DropColumn(session, table=SQL_PREFIX + subjschema.type, |
973 column=SQL_PREFIX + rschema.type) |
977 column=SQL_PREFIX + rschema.type) |
974 elif lastrel: |
978 elif lastrel: |
975 DropRelationTable(session, rschema.type) |
979 DropRelationTable(session, rschema.type) |
976 # if this is the last instance, drop associated relation type |
980 # if this is the last instance, drop associated relation type |
977 if lastrel and not rteid in pendings: |
981 if lastrel and not rteid in pendings: |
978 execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x') |
982 execute('DELETE CWRType X WHERE X eid %(x)s', {'x': self.eidto}, 'x') |
979 MemSchemaRDefDel(session, (subjschema, rschema, objschema)) |
983 MemSchemaRDefDel(session, (subjschema, rschema, objschema)) |
980 |
984 |
981 |
985 |
982 # CWAttribute / CWRelation hooks ############################################### |
986 # CWAttribute / CWRelation hooks ############################################### |
983 |
987 |
1085 if self._cw.describe(self.eidto)[0] == 'CWGroup': |
1089 if self._cw.describe(self.eidto)[0] == 'CWGroup': |
1086 MemSchemaPermissionAdd(self._cw, action=action, eid=self.eidfrom, |
1090 MemSchemaPermissionAdd(self._cw, action=action, eid=self.eidfrom, |
1087 group_eid=self.eidto) |
1091 group_eid=self.eidto) |
1088 else: # RQLExpression |
1092 else: # RQLExpression |
1089 expr = self._cw.entity_from_eid(self.eidto).expression |
1093 expr = self._cw.entity_from_eid(self.eidto).expression |
1090 MemSchemaPermissionAdd(session, action=action, eid=self.eidfrom, |
1094 MemSchemaPermissionAdd(self._cw, action=action, eid=self.eidfrom, |
1091 expr=expr) |
1095 expr=expr) |
1092 |
1096 |
1093 |
1097 |
1094 class BeforeDelPermissionHook(AfterAddPermissionHook): |
1098 class BeforeDelPermissionHook(AfterAddPermissionHook): |
1095 """delete entity/relation *_permission, need to update schema |
1099 """delete entity/relation *_permission, need to update schema |