hooks/syncschema.py
changeset 4011 394f853bb653
parent 4003 b9436fe77c9e
child 4042 f85a4c846aad
equal deleted inserted replaced
4010:b2d0b14a365d 4011:394f853bb653
    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