server/schemahooks.py
branchtls-sprint
changeset 1398 5fe84a5f7035
parent 1263 01152fffd593
child 1802 d628defebc17
equal deleted inserted replaced
1397:6cbc7bc8ea6d 1398:5fe84a5f7035
    21 from cubicweb.server.pool import Operation, SingleLastOperation, PreCommitOperation
    21 from cubicweb.server.pool import Operation, SingleLastOperation, PreCommitOperation
    22 from cubicweb.server.hookhelper import (entity_attr, entity_name,
    22 from cubicweb.server.hookhelper import (entity_attr, entity_name,
    23                                      check_internal_entity)
    23                                      check_internal_entity)
    24     
    24     
    25 # core entity and relation types which can't be removed
    25 # core entity and relation types which can't be removed
    26 CORE_ETYPES = list(BASE_TYPES) + ['EEType', 'ERType', 'EUser', 'EGroup',
    26 CORE_ETYPES = list(BASE_TYPES) + ['CWEType', 'CWRType', 'CWUser', 'CWGroup',
    27                                   'EConstraint', 'EFRDef', 'ENFRDef']
    27                                   'CWConstraint', 'CWAttribute', 'CWRelation']
    28 CORE_RTYPES = ['eid', 'creation_date', 'modification_date',
    28 CORE_RTYPES = ['eid', 'creation_date', 'modification_date',
    29                'login', 'upassword', 'name',
    29                'login', 'upassword', 'name',
    30                'is', 'instanceof', 'owned_by', 'created_by', 'in_group',
    30                'is', 'instanceof', 'owned_by', 'created_by', 'in_group',
    31                'relation_type', 'from_entity', 'to_entity',
    31                'relation_type', 'from_entity', 'to_entity',
    32                'constrainted_by',
    32                'constrainted_by',
   132             self.error('error while altering table %s: %s', table, ex)
   132             self.error('error while altering table %s: %s', table, ex)
   133             
   133             
   134 
   134 
   135 # deletion ####################################################################
   135 # deletion ####################################################################
   136 
   136 
   137 class DeleteEETypeOp(SchemaOperation):
   137 class DeleteCWETypeOp(SchemaOperation):
   138     """actually remove the entity type from the application's schema"""    
   138     """actually remove the entity type from the application's schema"""    
   139     def commit_event(self):
   139     def commit_event(self):
   140         try:
   140         try:
   141             # del_entity_type also removes entity's relations
   141             # del_entity_type also removes entity's relations
   142             self.schema.del_entity_type(self.kobj)
   142             self.schema.del_entity_type(self.kobj)
   143         except KeyError:
   143         except KeyError:
   144             # s/o entity type have already been deleted
   144             # s/o entity type have already been deleted
   145             pass
   145             pass
   146 
   146 
   147 def before_del_eetype(session, eid):
   147 def before_del_eetype(session, eid):
   148     """before deleting a EEType entity:
   148     """before deleting a CWEType entity:
   149     * check that we don't remove a core entity type
   149     * check that we don't remove a core entity type
   150     * cascade to delete related EFRDef and ENFRDef entities
   150     * cascade to delete related CWAttribute and CWRelation entities
   151     * instantiate an operation to delete the entity type on commit
   151     * instantiate an operation to delete the entity type on commit
   152     """
   152     """
   153     # final entities can't be deleted, don't care about that
   153     # final entities can't be deleted, don't care about that
   154     name = check_internal_entity(session, eid, CORE_ETYPES)
   154     name = check_internal_entity(session, eid, CORE_ETYPES)
   155     # delete every entities of this type
   155     # delete every entities of this type
   156     session.unsafe_execute('DELETE %s X' % name)
   156     session.unsafe_execute('DELETE %s X' % name)
   157     DropTableOp(session, table=SQL_PREFIX + name)
   157     DropTableOp(session, table=SQL_PREFIX + name)
   158     DeleteEETypeOp(session, name)
   158     DeleteCWETypeOp(session, name)
   159 
   159 
   160 def after_del_eetype(session, eid):
   160 def after_del_eetype(session, eid):
   161     # workflow cleanup
   161     # workflow cleanup
   162     session.execute('DELETE State X WHERE NOT X state_of Y')
   162     session.execute('DELETE State X WHERE NOT X state_of Y')
   163     session.execute('DELETE Transition X WHERE NOT X transition_of Y')
   163     session.execute('DELETE Transition X WHERE NOT X transition_of Y')
   164 
   164 
   165         
   165         
   166 class DeleteERTypeOp(SchemaOperation):
   166 class DeleteCWRTypeOp(SchemaOperation):
   167     """actually remove the relation type from the application's schema"""    
   167     """actually remove the relation type from the application's schema"""    
   168     def commit_event(self):
   168     def commit_event(self):
   169         try:
   169         try:
   170             self.schema.del_relation_type(self.kobj)
   170             self.schema.del_relation_type(self.kobj)
   171         except KeyError:
   171         except KeyError:
   172             # s/o entity type have already been deleted
   172             # s/o entity type have already been deleted
   173             pass
   173             pass
   174 
   174 
   175 def before_del_ertype(session, eid):
   175 def before_del_ertype(session, eid):
   176     """before deleting a ERType entity:
   176     """before deleting a CWRType entity:
   177     * check that we don't remove a core relation type
   177     * check that we don't remove a core relation type
   178     * cascade to delete related EFRDef and ENFRDef entities
   178     * cascade to delete related CWAttribute and CWRelation entities
   179     * instantiate an operation to delete the relation type on commit
   179     * instantiate an operation to delete the relation type on commit
   180     """
   180     """
   181     name = check_internal_entity(session, eid, CORE_RTYPES)
   181     name = check_internal_entity(session, eid, CORE_RTYPES)
   182     # delete relation definitions using this relation type
   182     # delete relation definitions using this relation type
   183     session.execute('DELETE EFRDef X WHERE X relation_type Y, Y eid %(x)s',
   183     session.execute('DELETE CWAttribute X WHERE X relation_type Y, Y eid %(x)s',
   184                     {'x': eid})
   184                     {'x': eid})
   185     session.execute('DELETE ENFRDef X WHERE X relation_type Y, Y eid %(x)s',
   185     session.execute('DELETE CWRelation X WHERE X relation_type Y, Y eid %(x)s',
   186                     {'x': eid})
   186                     {'x': eid})
   187     DeleteERTypeOp(session, name)
   187     DeleteCWRTypeOp(session, name)
   188 
   188 
   189     
   189     
   190 class DelErdefOp(SchemaOperation):
   190 class DelErdefOp(SchemaOperation):
   191     """actually remove the relation definition from the application's schema"""
   191     """actually remove the relation definition from the application's schema"""
   192     def commit_event(self):
   192     def commit_event(self):
   196         except KeyError:
   196         except KeyError:
   197             # relation type may have been already deleted
   197             # relation type may have been already deleted
   198             pass
   198             pass
   199         
   199         
   200 def after_del_relation_type(session, rdefeid, rtype, rteid):
   200 def after_del_relation_type(session, rdefeid, rtype, rteid):
   201     """before deleting a EFRDef or ENFRDef entity:
   201     """before deleting a CWAttribute or CWRelation entity:
   202     * if this is a final or inlined relation definition, instantiate an
   202     * if this is a final or inlined relation definition, instantiate an
   203       operation to drop necessary column, else if this is the last instance
   203       operation to drop necessary column, else if this is the last instance
   204       of a non final relation, instantiate an operation to drop necessary
   204       of a non final relation, instantiate an operation to drop necessary
   205       table
   205       table
   206     * instantiate an operation to delete the relation definition on commit
   206     * instantiate an operation to delete the relation definition on commit
   208     """
   208     """
   209     subjschema, rschema, objschema = session.repo.schema.schema_by_eid(rdefeid)
   209     subjschema, rschema, objschema = session.repo.schema.schema_by_eid(rdefeid)
   210     pendings = session.query_data('pendingeids', ())
   210     pendings = session.query_data('pendingeids', ())
   211     # first delete existing relation if necessary
   211     # first delete existing relation if necessary
   212     if rschema.is_final():
   212     if rschema.is_final():
   213         rdeftype = 'EFRDef'
   213         rdeftype = 'CWAttribute'
   214     else:
   214     else:
   215         rdeftype = 'ENFRDef'
   215         rdeftype = 'CWRelation'
   216         if not (subjschema.eid in pendings or objschema.eid in pendings):
   216         if not (subjschema.eid in pendings or objschema.eid in pendings):
   217             session.execute('DELETE X %s Y WHERE X is %s, Y is %s'
   217             session.execute('DELETE X %s Y WHERE X is %s, Y is %s'
   218                             % (rschema, subjschema, objschema))
   218                             % (rschema, subjschema, objschema))
   219     execute = session.unsafe_execute
   219     execute = session.unsafe_execute
   220     rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R,'
   220     rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R,'
   233                          column=SQL_PREFIX + rschema.type)
   233                          column=SQL_PREFIX + rschema.type)
   234     elif lastrel:
   234     elif lastrel:
   235         DropTableOp(session, table='%s_relation' % rschema.type)
   235         DropTableOp(session, table='%s_relation' % rschema.type)
   236     # if this is the last instance, drop associated relation type
   236     # if this is the last instance, drop associated relation type
   237     if lastrel and not rteid in pendings:
   237     if lastrel and not rteid in pendings:
   238         execute('DELETE ERType X WHERE X eid %(x)s', {'x': rteid}, 'x')
   238         execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x')
   239     DelErdefOp(session, (subjschema, rschema, objschema))
   239     DelErdefOp(session, (subjschema, rschema, objschema))
   240 
   240 
   241         
   241         
   242 # addition ####################################################################
   242 # addition ####################################################################
   243 
   243 
   244 class AddEETypeOp(EarlySchemaOperation):
   244 class AddCWETypeOp(EarlySchemaOperation):
   245     """actually add the entity type to the application's schema"""    
   245     """actually add the entity type to the application's schema"""    
   246     eid = None # make pylint happy
   246     eid = None # make pylint happy
   247     def commit_event(self):
   247     def commit_event(self):
   248         eschema = self.schema.add_entity_type(self.kobj)
   248         eschema = self.schema.add_entity_type(self.kobj)
   249         eschema.eid = self.eid
   249         eschema.eid = self.eid
   250         
   250         
   251 def before_add_eetype(session, entity):
   251 def before_add_eetype(session, entity):
   252     """before adding a EEType entity:
   252     """before adding a CWEType entity:
   253     * check that we are not using an existing entity type,
   253     * check that we are not using an existing entity type,
   254     """
   254     """
   255     name = entity['name']
   255     name = entity['name']
   256     schema = session.repo.schema
   256     schema = session.repo.schema
   257     if name in schema and schema[name].eid is not None:
   257     if name in schema and schema[name].eid is not None:
   258         raise RepositoryError('an entity type %s already exists' % name)
   258         raise RepositoryError('an entity type %s already exists' % name)
   259 
   259 
   260 def after_add_eetype(session, entity):
   260 def after_add_eetype(session, entity):
   261     """after adding a EEType entity:
   261     """after adding a CWEType entity:
   262     * create the necessary table
   262     * create the necessary table
   263     * set creation_date and modification_date by creating the necessary
   263     * set creation_date and modification_date by creating the necessary
   264       EFRDef entities
   264       CWAttribute entities
   265     * add owned_by relation by creating the necessary ENFRDef entity
   265     * add owned_by relation by creating the necessary CWRelation entity
   266     * register an operation to add the entity type to the application's
   266     * register an operation to add the entity type to the application's
   267       schema on commit
   267       schema on commit
   268     """
   268     """
   269     if entity.get('final'):
   269     if entity.get('final'):
   270         return
   270         return
   295         if sql.strip():
   295         if sql.strip():
   296             session.system_sql(sql)
   296             session.system_sql(sql)
   297     # register operation to modify the schema on commit
   297     # register operation to modify the schema on commit
   298     # this have to be done before adding other relations definitions
   298     # this have to be done before adding other relations definitions
   299     # or permission settings
   299     # or permission settings
   300     AddEETypeOp(session, etype, eid=entity.eid)
   300     AddCWETypeOp(session, etype, eid=entity.eid)
   301     # add meta creation_date, modification_date and owned_by relations
   301     # add meta creation_date, modification_date and owned_by relations
   302     for rql, kwargs in relrqls:
   302     for rql, kwargs in relrqls:
   303         session.execute(rql, kwargs)
   303         session.execute(rql, kwargs)
   304 
   304 
   305 
   305 
   306 class AddERTypeOp(EarlySchemaOperation):
   306 class AddCWRTypeOp(EarlySchemaOperation):
   307     """actually add the relation type to the application's schema"""    
   307     """actually add the relation type to the application's schema"""    
   308     eid = None # make pylint happy
   308     eid = None # make pylint happy
   309     def commit_event(self):
   309     def commit_event(self):
   310         rschema = self.schema.add_relation_type(self.kobj)
   310         rschema = self.schema.add_relation_type(self.kobj)
   311         rschema.set_default_groups()
   311         rschema.set_default_groups()
   312         rschema.eid = self.eid
   312         rschema.eid = self.eid
   313         
   313         
   314 def before_add_ertype(session, entity):
   314 def before_add_ertype(session, entity):
   315     """before adding a ERType entity:
   315     """before adding a CWRType entity:
   316     * check that we are not using an existing relation type,
   316     * check that we are not using an existing relation type,
   317     * register an operation to add the relation type to the application's
   317     * register an operation to add the relation type to the application's
   318       schema on commit
   318       schema on commit
   319       
   319       
   320     We don't know yeat this point if a table is necessary
   320     We don't know yeat this point if a table is necessary
   322     name = entity['name']
   322     name = entity['name']
   323     if name in session.repo.schema.relations():
   323     if name in session.repo.schema.relations():
   324         raise RepositoryError('a relation type %s already exists' % name)
   324         raise RepositoryError('a relation type %s already exists' % name)
   325     
   325     
   326 def after_add_ertype(session, entity):
   326 def after_add_ertype(session, entity):
   327     """after a ERType entity has been added:
   327     """after a CWRType entity has been added:
   328     * register an operation to add the relation type to the application's
   328     * register an operation to add the relation type to the application's
   329       schema on commit
   329       schema on commit
   330     We don't know yeat this point if a table is necessary
   330     We don't know yeat this point if a table is necessary
   331     """
   331     """
   332     AddERTypeOp(session, RelationType(name=entity['name'],
   332     AddCWRTypeOp(session, RelationType(name=entity['name'],
   333                                       description=entity.get('description'),
   333                                       description=entity.get('description'),
   334                                       meta=entity.get('meta', False),
   334                                       meta=entity.get('meta', False),
   335                                       inlined=entity.get('inlined', False),
   335                                       inlined=entity.get('inlined', False),
   336                                       symetric=entity.get('symetric', False)),
   336                                       symetric=entity.get('symetric', False)),
   337                 eid=entity.eid)
   337                 eid=entity.eid)
   354     'Datetime' : unicode,
   354     'Datetime' : unicode,
   355     'Time' : unicode,
   355     'Time' : unicode,
   356     }
   356     }
   357 
   357 
   358 
   358 
   359 class AddEFRDefPreCommitOp(PreCommitOperation):
   359 class AddCWAttributePreCommitOp(PreCommitOperation):
   360     """an attribute relation (EFRDef) has been added:
   360     """an attribute relation (CWAttribute) has been added:
   361     * add the necessary column
   361     * add the necessary column
   362     * set default on this column if any and possible
   362     * set default on this column if any and possible
   363     * register an operation to add the relation definition to the
   363     * register an operation to add the relation definition to the
   364       application's schema on commit
   364       application's schema on commit
   365       
   365       
   437             session.system_sql('UPDATE %s SET %s=%%(default)s' % (table, column),
   437             session.system_sql('UPDATE %s SET %s=%%(default)s' % (table, column),
   438                                {'default': default})
   438                                {'default': default})
   439         AddErdefOp(session, rdef)
   439         AddErdefOp(session, rdef)
   440 
   440 
   441 def after_add_efrdef(session, entity):
   441 def after_add_efrdef(session, entity):
   442     AddEFRDefPreCommitOp(session, entity=entity)
   442     AddCWAttributePreCommitOp(session, entity=entity)
   443 
   443 
   444 
   444 
   445 class AddENFRDefPreCommitOp(PreCommitOperation):
   445 class AddCWRelationPreCommitOp(PreCommitOperation):
   446     """an actual relation has been added:
   446     """an actual relation has been added:
   447     * if this is an inlined relation, add the necessary column
   447     * if this is an inlined relation, add the necessary column
   448       else if it's the first instance of this relation type, add the
   448       else if it's the first instance of this relation type, add the
   449       necessary table and set default permissions
   449       necessary table and set default permissions
   450     * register an operation to add the relation definition to the
   450     * register an operation to add the relation definition to the
   508                     if sql.strip():
   508                     if sql.strip():
   509                         self.session.system_sql(sql)
   509                         self.session.system_sql(sql)
   510                 session.add_query_data('createdtables', rtype)
   510                 session.add_query_data('createdtables', rtype)
   511                 
   511                 
   512 def after_add_enfrdef(session, entity):
   512 def after_add_enfrdef(session, entity):
   513     AddENFRDefPreCommitOp(session, entity=entity)
   513     AddCWRelationPreCommitOp(session, entity=entity)
   514 
   514 
   515 
   515 
   516 # update ######################################################################
   516 # update ######################################################################
   517 
   517 
   518 def check_valid_changes(session, entity, ro_attrs=('name', 'final')):
   518 def check_valid_changes(session, entity, ro_attrs=('name', 'final')):
   844         erschema.set_rqlexprs(self.perm, exprs)
   844         erschema.set_rqlexprs(self.perm, exprs)
   845 
   845 
   846 def after_add_permission(session, subject, rtype, object):
   846 def after_add_permission(session, subject, rtype, object):
   847     """added entity/relation *_permission, need to update schema"""
   847     """added entity/relation *_permission, need to update schema"""
   848     perm = rtype.split('_', 1)[0]
   848     perm = rtype.split('_', 1)[0]
   849     if session.describe(object)[0] == 'EGroup':
   849     if session.describe(object)[0] == 'CWGroup':
   850         AddGroupPermissionOp(session, perm, subject, object)
   850         AddGroupPermissionOp(session, perm, subject, object)
   851     else: # RQLExpression
   851     else: # RQLExpression
   852         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
   852         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
   853                                {'x': object}, 'x')[0][0]
   853                                {'x': object}, 'x')[0][0]
   854         AddRQLExpressionPermissionOp(session, perm, subject, expr)
   854         AddRQLExpressionPermissionOp(session, perm, subject, expr)
   904     skip the operation if the related type is being deleted
   904     skip the operation if the related type is being deleted
   905     """
   905     """
   906     if subject in session.query_data('pendingeids', ()):
   906     if subject in session.query_data('pendingeids', ()):
   907         return
   907         return
   908     perm = rtype.split('_', 1)[0]
   908     perm = rtype.split('_', 1)[0]
   909     if session.describe(object)[0] == 'EGroup':
   909     if session.describe(object)[0] == 'CWGroup':
   910         DelGroupPermissionOp(session, perm, subject, object)
   910         DelGroupPermissionOp(session, perm, subject, object)
   911     else: # RQLExpression
   911     else: # RQLExpression
   912         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
   912         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
   913                                {'x': object}, 'x')[0][0]
   913                                {'x': object}, 'x')[0][0]
   914         DelRQLExpressionPermissionOp(session, perm, subject, expr)
   914         DelRQLExpressionPermissionOp(session, perm, subject, expr)
   923 
   923 
   924 def _register_schema_hooks(hm):
   924 def _register_schema_hooks(hm):
   925     """register schema related hooks on the hooks manager"""
   925     """register schema related hooks on the hooks manager"""
   926     # schema synchronisation #####################
   926     # schema synchronisation #####################
   927     # before/after add
   927     # before/after add
   928     hm.register_hook(before_add_eetype, 'before_add_entity', 'EEType')
   928     hm.register_hook(before_add_eetype, 'before_add_entity', 'CWEType')
   929     hm.register_hook(before_add_ertype, 'before_add_entity', 'ERType')
   929     hm.register_hook(before_add_ertype, 'before_add_entity', 'CWRType')
   930     hm.register_hook(after_add_eetype, 'after_add_entity', 'EEType')
   930     hm.register_hook(after_add_eetype, 'after_add_entity', 'CWEType')
   931     hm.register_hook(after_add_ertype, 'after_add_entity', 'ERType')
   931     hm.register_hook(after_add_ertype, 'after_add_entity', 'CWRType')
   932     hm.register_hook(after_add_efrdef, 'after_add_entity', 'EFRDef')
   932     hm.register_hook(after_add_efrdef, 'after_add_entity', 'CWAttribute')
   933     hm.register_hook(after_add_enfrdef, 'after_add_entity', 'ENFRDef')
   933     hm.register_hook(after_add_enfrdef, 'after_add_entity', 'CWRelation')
   934     # before/after update
   934     # before/after update
   935     hm.register_hook(before_update_eetype, 'before_update_entity', 'EEType')
   935     hm.register_hook(before_update_eetype, 'before_update_entity', 'CWEType')
   936     hm.register_hook(before_update_ertype, 'before_update_entity', 'ERType')
   936     hm.register_hook(before_update_ertype, 'before_update_entity', 'CWRType')
   937     hm.register_hook(after_update_ertype, 'after_update_entity', 'ERType')
   937     hm.register_hook(after_update_ertype, 'after_update_entity', 'CWRType')
   938     hm.register_hook(after_update_erdef, 'after_update_entity', 'EFRDef')
   938     hm.register_hook(after_update_erdef, 'after_update_entity', 'CWAttribute')
   939     hm.register_hook(after_update_erdef, 'after_update_entity', 'ENFRDef')
   939     hm.register_hook(after_update_erdef, 'after_update_entity', 'CWRelation')
   940     # before/after delete
   940     # before/after delete
   941     hm.register_hook(before_del_eetype, 'before_delete_entity', 'EEType')
   941     hm.register_hook(before_del_eetype, 'before_delete_entity', 'CWEType')
   942     hm.register_hook(after_del_eetype, 'after_delete_entity', 'EEType')
   942     hm.register_hook(after_del_eetype, 'after_delete_entity', 'CWEType')
   943     hm.register_hook(before_del_ertype, 'before_delete_entity', 'ERType')
   943     hm.register_hook(before_del_ertype, 'before_delete_entity', 'CWRType')
   944     hm.register_hook(after_del_relation_type, 'after_delete_relation', 'relation_type')
   944     hm.register_hook(after_del_relation_type, 'after_delete_relation', 'relation_type')
   945     hm.register_hook(rebuild_infered_relations, 'after_add_relation', 'specializes')
   945     hm.register_hook(rebuild_infered_relations, 'after_add_relation', 'specializes')
   946     hm.register_hook(rebuild_infered_relations, 'after_delete_relation', 'specializes')    
   946     hm.register_hook(rebuild_infered_relations, 'after_delete_relation', 'specializes')    
   947     # constraints synchronization hooks
   947     # constraints synchronization hooks
   948     hm.register_hook(after_add_econstraint, 'after_add_entity', 'EConstraint')
   948     hm.register_hook(after_add_econstraint, 'after_add_entity', 'CWConstraint')
   949     hm.register_hook(after_update_econstraint, 'after_update_entity', 'EConstraint')
   949     hm.register_hook(after_update_econstraint, 'after_update_entity', 'CWConstraint')
   950     hm.register_hook(before_delete_constrained_by, 'before_delete_relation', 'constrained_by')
   950     hm.register_hook(before_delete_constrained_by, 'before_delete_relation', 'constrained_by')
   951     hm.register_hook(after_add_constrained_by, 'after_add_relation', 'constrained_by')
   951     hm.register_hook(after_add_constrained_by, 'after_add_relation', 'constrained_by')
   952     # permissions synchronisation ################
   952     # permissions synchronisation ################
   953     for perm in ('read_permission', 'add_permission',
   953     for perm in ('read_permission', 'add_permission',
   954                  'delete_permission', 'update_permission'):
   954                  'delete_permission', 'update_permission'):