19 from cubicweb.server import schemaserial as ss |
19 from cubicweb.server import schemaserial as ss |
20 from cubicweb.server.sqlutils import SQL_PREFIX |
20 from cubicweb.server.sqlutils import SQL_PREFIX |
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) + ['CWEType', 'CWRType', 'CWUser', 'CWGroup', |
26 CORE_ETYPES = list(BASE_TYPES) + ['CWEType', 'CWRType', 'CWUser', 'CWGroup', |
27 'CWConstraint', 'CWAttribute', 'CWRelation'] |
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', |
50 try: |
50 try: |
51 session.system_sql(str('ALTER TABLE %s ADD COLUMN %s integer' |
51 session.system_sql(str('ALTER TABLE %s ADD COLUMN %s integer' |
52 % (table, column))) |
52 % (table, column))) |
53 session.info('added column %s to table %s', column, table) |
53 session.info('added column %s to table %s', column, table) |
54 except: |
54 except: |
55 # silent exception here, if this error has not been raised because the |
55 # silent exception here, if this error has not been raised because the |
56 # column already exists, index creation will fail anyway |
56 # column already exists, index creation will fail anyway |
57 session.exception('error while adding column %s to table %s', |
57 session.exception('error while adding column %s to table %s', |
58 table, column) |
58 table, column) |
59 # create index before alter table which may expectingly fail during test |
59 # create index before alter table which may expectingly fail during test |
60 # (sqlite) while index creation should never fail (test for index existence |
60 # (sqlite) while index creation should never fail (test for index existence |
72 # once Operation.__init__ has been called, event may be triggered, so |
72 # once Operation.__init__ has been called, event may be triggered, so |
73 # do this last ! |
73 # do this last ! |
74 Operation.__init__(self, session, **kwargs) |
74 Operation.__init__(self, session, **kwargs) |
75 # every schema operation is triggering a schema update |
75 # every schema operation is triggering a schema update |
76 UpdateSchemaOp(session) |
76 UpdateSchemaOp(session) |
77 |
77 |
78 class EarlySchemaOperation(SchemaOperation): |
78 class EarlySchemaOperation(SchemaOperation): |
79 def insert_index(self): |
79 def insert_index(self): |
80 """schema operation which are inserted at the begining of the queue |
80 """schema operation which are inserted at the begining of the queue |
81 (typically to add/remove entity or relation types) |
81 (typically to add/remove entity or relation types) |
82 """ |
82 """ |
83 i = -1 |
83 i = -1 |
84 for i, op in enumerate(self.session.pending_operations): |
84 for i, op in enumerate(self.session.pending_operations): |
85 if not isinstance(op, EarlySchemaOperation): |
85 if not isinstance(op, EarlySchemaOperation): |
86 return i |
86 return i |
87 return i + 1 |
87 return i + 1 |
88 |
88 |
89 class UpdateSchemaOp(SingleLastOperation): |
89 class UpdateSchemaOp(SingleLastOperation): |
90 """the update schema operation: |
90 """the update schema operation: |
91 |
91 |
92 special operation which should be called once and after all other schema |
92 special operation which should be called once and after all other schema |
93 operations. It will trigger internal structures rebuilding to consider |
93 operations. It will trigger internal structures rebuilding to consider |
94 schema changes |
94 schema changes |
95 """ |
95 """ |
96 |
96 |
97 def __init__(self, session): |
97 def __init__(self, session): |
98 self.repo = session.repo |
98 self.repo = session.repo |
99 SingleLastOperation.__init__(self, session) |
99 SingleLastOperation.__init__(self, session) |
100 |
100 |
101 def commit_event(self): |
101 def commit_event(self): |
102 self.repo.set_schema(self.repo.schema) |
102 self.repo.set_schema(self.repo.schema) |
103 |
103 |
104 |
104 |
105 class DropTableOp(PreCommitOperation): |
105 class DropTableOp(PreCommitOperation): |
106 """actually remove a database from the application's schema""" |
106 """actually remove a database from the application's schema""" |
107 table = None # make pylint happy |
107 table = None # make pylint happy |
108 def precommit_event(self): |
108 def precommit_event(self): |
109 dropped = self.session.query_data('droppedtables', |
109 dropped = self.session.query_data('droppedtables', |
111 if self.table in dropped: |
111 if self.table in dropped: |
112 return # already processed |
112 return # already processed |
113 dropped.add(self.table) |
113 dropped.add(self.table) |
114 self.session.system_sql('DROP TABLE %s' % self.table) |
114 self.session.system_sql('DROP TABLE %s' % self.table) |
115 self.info('dropped table %s', self.table) |
115 self.info('dropped table %s', self.table) |
116 |
116 |
117 class DropColumnOp(PreCommitOperation): |
117 class DropColumnOp(PreCommitOperation): |
118 """actually remove the attribut's column from entity table in the system |
118 """actually remove the attribut's column from entity table in the system |
119 database |
119 database |
120 """ |
120 """ |
121 table = column = None # make pylint happy |
121 table = column = None # make pylint happy |
128 % (table, column)) |
128 % (table, column)) |
129 self.info('dropped column %s from table %s', column, table) |
129 self.info('dropped column %s from table %s', column, table) |
130 except Exception, ex: |
130 except Exception, ex: |
131 # not supported by sqlite for instance |
131 # not supported by sqlite for instance |
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 DeleteCWETypeOp(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: |
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 DeleteCWRTypeOp(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 |
184 {'x': eid}) |
184 {'x': eid}) |
185 session.execute('DELETE CWRelation 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 DeleteCWRTypeOp(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): |
193 subjtype, rtype, objtype = self.kobj |
193 subjtype, rtype, objtype = self.kobj |
194 try: |
194 try: |
195 self.schema.del_relation_def(subjtype, rtype, objtype) |
195 self.schema.del_relation_def(subjtype, rtype, objtype) |
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 CWAttribute or CWRelation 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 |
221 'R eid %%(x)s' % rdeftype, {'x': rteid}) |
221 'R eid %%(x)s' % rdeftype, {'x': rteid}) |
222 lastrel = rset[0][0] == 0 |
222 lastrel = rset[0][0] == 0 |
223 # we have to update physical schema systematically for final and inlined |
223 # we have to update physical schema systematically for final and inlined |
224 # relations, but only if it's the last instance for this relation type |
224 # relations, but only if it's the last instance for this relation type |
225 # for other relations |
225 # for other relations |
226 |
226 |
227 if (rschema.is_final() or rschema.inlined): |
227 if (rschema.is_final() or rschema.inlined): |
228 rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R, ' |
228 rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R, ' |
229 'R eid %%(x)s, X from_entity E, E name %%(name)s' |
229 'R eid %%(x)s, X from_entity E, E name %%(name)s' |
230 % rdeftype, {'x': rteid, 'name': str(subjschema)}) |
230 % rdeftype, {'x': rteid, 'name': str(subjschema)}) |
231 if rset[0][0] == 0 and not subjschema.eid in pendings: |
231 if rset[0][0] == 0 and not subjschema.eid in pendings: |
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 CWRType 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 AddCWETypeOp(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 CWEType 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'] |
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 AddCWRTypeOp(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 CWRType 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 |
321 """ |
321 """ |
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 CWRType 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 |
360 """an attribute relation (CWAttribute) 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 |
366 constraints are handled by specific hooks |
366 constraints are handled by specific hooks |
367 """ |
367 """ |
368 entity = None # make pylint happy |
368 entity = None # make pylint happy |
369 def precommit_event(self): |
369 def precommit_event(self): |
370 session = self.session |
370 session = self.session |
455 entity = None # make pylint happy |
455 entity = None # make pylint happy |
456 def precommit_event(self): |
456 def precommit_event(self): |
457 session = self.session |
457 session = self.session |
458 entity = self.entity |
458 entity = self.entity |
459 fromentity = entity.from_entity[0] |
459 fromentity = entity.from_entity[0] |
460 relationtype = entity.relation_type[0] |
460 relationtype = entity.relation_type[0] |
461 session.execute('SET X ordernum Y+1 WHERE X from_entity SE, SE eid %(se)s, X ordernum Y, X ordernum >= %(order)s, NOT X eid %(x)s', |
461 session.execute('SET X ordernum Y+1 WHERE X from_entity SE, SE eid %(se)s, X ordernum Y, X ordernum >= %(order)s, NOT X eid %(x)s', |
462 {'x': entity.eid, 'se': fromentity.eid, 'order': entity.ordernum or 0}) |
462 {'x': entity.eid, 'se': fromentity.eid, 'order': entity.ordernum or 0}) |
463 subj, rtype = str(fromentity.name), str(relationtype.name) |
463 subj, rtype = str(fromentity.name), str(relationtype.name) |
464 obj = str(entity.to_entity[0].name) |
464 obj = str(entity.to_entity[0].name) |
465 card = entity.get('cardinality') |
465 card = entity.get('cardinality') |
506 # create the necessary table |
506 # create the necessary table |
507 for sql in tablesql.split(';'): |
507 for sql in tablesql.split(';'): |
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 AddCWRelationPreCommitOp(session, entity=entity) |
513 AddCWRelationPreCommitOp(session, entity=entity) |
514 |
514 |
515 |
515 |
516 # update ###################################################################### |
516 # update ###################################################################### |
554 self.info('renamed table %s to %s', self.oldname, self.newname) |
554 self.info('renamed table %s to %s', self.oldname, self.newname) |
555 sqlexec('UPDATE entities SET type=%s WHERE type=%s', |
555 sqlexec('UPDATE entities SET type=%s WHERE type=%s', |
556 (self.newname, self.oldname)) |
556 (self.newname, self.oldname)) |
557 sqlexec('UPDATE deleted_entities SET type=%s WHERE type=%s', |
557 sqlexec('UPDATE deleted_entities SET type=%s WHERE type=%s', |
558 (self.newname, self.oldname)) |
558 (self.newname, self.oldname)) |
559 |
559 |
560 def commit_event(self): |
560 def commit_event(self): |
561 self.session.repo.schema.rename_entity_type(self.oldname, self.newname) |
561 self.session.repo.schema.rename_entity_type(self.oldname, self.newname) |
562 |
562 |
563 |
563 |
564 class UpdateRdefOp(SchemaOperation): |
564 class UpdateRdefOp(SchemaOperation): |
573 column = SQL_PREFIX + rtype |
573 column = SQL_PREFIX + rtype |
574 if self.values['indexed']: |
574 if self.values['indexed']: |
575 sysource.create_index(self.session, table, column) |
575 sysource.create_index(self.session, table, column) |
576 else: |
576 else: |
577 sysource.drop_index(self.session, table, column) |
577 sysource.drop_index(self.session, table, column) |
578 |
578 |
579 def commit_event(self): |
579 def commit_event(self): |
580 # structure should be clean, not need to remove entity's relations |
580 # structure should be clean, not need to remove entity's relations |
581 # at this point |
581 # at this point |
582 self.rschema._rproperties[self.kobj].update(self.values) |
582 self.rschema._rproperties[self.kobj].update(self.values) |
583 |
583 |
584 |
584 |
585 def after_update_erdef(session, entity): |
585 def after_update_erdef(session, entity): |
586 desttype = entity.to_entity[0].name |
586 desttype = entity.to_entity[0].name |
587 rschema = session.repo.schema[entity.relation_type[0].name] |
587 rschema = session.repo.schema[entity.relation_type[0].name] |
588 newvalues = {} |
588 newvalues = {} |
589 for prop in rschema.rproperty_defs(desttype): |
589 for prop in rschema.rproperty_defs(desttype): |
598 UpdateRdefOp(session, (subjtype, desttype), rschema=rschema, |
598 UpdateRdefOp(session, (subjtype, desttype), rschema=rschema, |
599 values=newvalues) |
599 values=newvalues) |
600 |
600 |
601 |
601 |
602 class UpdateRtypeOp(SchemaOperation): |
602 class UpdateRtypeOp(SchemaOperation): |
603 """actually update some properties of a relation definition""" |
603 """actually update some properties of a relation definition""" |
604 rschema = values = entity = None # make pylint happy |
604 rschema = values = entity = None # make pylint happy |
605 |
605 |
606 def precommit_event(self): |
606 def precommit_event(self): |
607 session = self.session |
607 session = self.session |
608 rschema = self.rschema |
608 rschema = self.rschema |
637 DropColumnOp(session, table=SQL_PREFIX + str(etype), |
637 DropColumnOp(session, table=SQL_PREFIX + str(etype), |
638 column=SQL_PREFIX + rtype) |
638 column=SQL_PREFIX + rtype) |
639 else: |
639 else: |
640 for etype in rschema.subjects(): |
640 for etype in rschema.subjects(): |
641 try: |
641 try: |
642 add_inline_relation_column(session, str(etype), rtype) |
642 add_inline_relation_column(session, str(etype), rtype) |
643 except Exception, ex: |
643 except Exception, ex: |
644 # the column probably already exists. this occurs when |
644 # the column probably already exists. this occurs when |
645 # the entity's type has just been added or if the column |
645 # the entity's type has just been added or if the column |
646 # has not been previously dropped |
646 # has not been previously dropped |
647 self.error('error while altering table %s: %s', etype, ex) |
647 self.error('error while altering table %s: %s', etype, ex) |
648 # copy existant data. |
648 # copy existant data. |
649 # XXX don't use, it's not supported by sqlite (at least at when i tried it) |
649 # XXX don't use, it's not supported by sqlite (at least at when i tried it) |
650 #sqlexec('UPDATE %(etype)s SET %(rtype)s=eid_to ' |
650 #sqlexec('UPDATE %(etype)s SET %(rtype)s=eid_to ' |
651 # 'FROM %(rtype)s_relation ' |
651 # 'FROM %(rtype)s_relation ' |
652 # 'WHERE %(etype)s.eid=%(rtype)s_relation.eid_from' |
652 # 'WHERE %(etype)s.eid=%(rtype)s_relation.eid_from' |
653 # % locals()) |
653 # % locals()) |
665 |
665 |
666 def commit_event(self): |
666 def commit_event(self): |
667 # structure should be clean, not need to remove entity's relations |
667 # structure should be clean, not need to remove entity's relations |
668 # at this point |
668 # at this point |
669 self.rschema.__dict__.update(self.values) |
669 self.rschema.__dict__.update(self.values) |
670 |
670 |
671 def after_update_ertype(session, entity): |
671 def after_update_ertype(session, entity): |
672 rschema = session.repo.schema.rschema(entity.name) |
672 rschema = session.repo.schema.rschema(entity.name) |
673 newvalues = {} |
673 newvalues = {} |
674 for prop in ('meta', 'symetric', 'inlined'): |
674 for prop in ('meta', 'symetric', 'inlined'): |
675 if prop in entity: |
675 if prop in entity: |
682 from cubicweb.schema import CONSTRAINTS |
682 from cubicweb.schema import CONSTRAINTS |
683 |
683 |
684 class ConstraintOp(SchemaOperation): |
684 class ConstraintOp(SchemaOperation): |
685 """actually update constraint of a relation definition""" |
685 """actually update constraint of a relation definition""" |
686 entity = None # make pylint happy |
686 entity = None # make pylint happy |
687 |
687 |
688 def prepare_constraints(self, rtype, subjtype, objtype): |
688 def prepare_constraints(self, rtype, subjtype, objtype): |
689 constraints = rtype.rproperty(subjtype, objtype, 'constraints') |
689 constraints = rtype.rproperty(subjtype, objtype, 'constraints') |
690 self.constraints = list(constraints) |
690 self.constraints = list(constraints) |
691 rtype.set_rproperty(subjtype, objtype, 'constraints', self.constraints) |
691 rtype.set_rproperty(subjtype, objtype, 'constraints', self.constraints) |
692 return self.constraints |
692 return self.constraints |
693 |
693 |
694 def precommit_event(self): |
694 def precommit_event(self): |
695 rdef = self.entity.reverse_constrained_by[0] |
695 rdef = self.entity.reverse_constrained_by[0] |
696 session = self.session |
696 session = self.session |
697 # when the relation is added in the same transaction, the constraint object |
697 # when the relation is added in the same transaction, the constraint object |
698 # is created by AddEN?FRDefPreCommitOp, there is nothing to do here |
698 # is created by AddEN?FRDefPreCommitOp, there is nothing to do here |
699 if rdef.eid in session.query_data('neweids', ()): |
699 if rdef.eid in session.query_data('neweids', ()): |
700 self.cancelled = True |
700 self.cancelled = True |
701 return |
701 return |
702 self.cancelled = False |
702 self.cancelled = False |
703 schema = session.repo.schema |
703 schema = session.repo.schema |
704 subjtype, rtype, objtype = schema.schema_by_eid(rdef.eid) |
704 subjtype, rtype, objtype = schema.schema_by_eid(rdef.eid) |
705 self.prepare_constraints(rtype, subjtype, objtype) |
705 self.prepare_constraints(rtype, subjtype, objtype) |
706 cstrtype = self.entity.type |
706 cstrtype = self.entity.type |
721 # not supported by sqlite for instance |
721 # not supported by sqlite for instance |
722 self.error('error while altering table %s: %s', table, ex) |
722 self.error('error while altering table %s: %s', table, ex) |
723 elif cstrtype == 'UniqueConstraint': |
723 elif cstrtype == 'UniqueConstraint': |
724 session.pool.source('system').create_index( |
724 session.pool.source('system').create_index( |
725 self.session, table, column, unique=True) |
725 self.session, table, column, unique=True) |
726 |
726 |
727 def commit_event(self): |
727 def commit_event(self): |
728 if self.cancelled: |
728 if self.cancelled: |
729 return |
729 return |
730 # in-place removing |
730 # in-place removing |
731 if not self.cstr is None: |
731 if not self.cstr is None: |
741 |
741 |
742 |
742 |
743 class DelConstraintOp(ConstraintOp): |
743 class DelConstraintOp(ConstraintOp): |
744 """actually remove a constraint of a relation definition""" |
744 """actually remove a constraint of a relation definition""" |
745 rtype = subjtype = objtype = None # make pylint happy |
745 rtype = subjtype = objtype = None # make pylint happy |
746 |
746 |
747 def precommit_event(self): |
747 def precommit_event(self): |
748 self.prepare_constraints(self.rtype, self.subjtype, self.objtype) |
748 self.prepare_constraints(self.rtype, self.subjtype, self.objtype) |
749 cstrtype = self.cstr.type() |
749 cstrtype = self.cstr.type() |
750 table = SQL_PREFIX + str(self.subjtype) |
750 table = SQL_PREFIX + str(self.subjtype) |
751 column = SQL_PREFIX + str(self.rtype) |
751 column = SQL_PREFIX + str(self.rtype) |
752 # alter the physical schema on size/unique constraint changes |
752 # alter the physical schema on size/unique constraint changes |
753 if cstrtype == 'SizeConstraint': |
753 if cstrtype == 'SizeConstraint': |
754 try: |
754 try: |
755 self.session.system_sql('ALTER TABLE %s ALTER COLUMN %s TYPE TEXT' |
755 self.session.system_sql('ALTER TABLE %s ALTER COLUMN %s TYPE TEXT' |
756 % (table, column)) |
756 % (table, column)) |
757 self.info('altered column %s of table %s: now TEXT', |
757 self.info('altered column %s of table %s: now TEXT', |
758 column, table) |
758 column, table) |
759 except Exception, ex: |
759 except Exception, ex: |
760 # not supported by sqlite for instance |
760 # not supported by sqlite for instance |
761 self.error('error while altering table %s: %s', table, ex) |
761 self.error('error while altering table %s: %s', table, ex) |
762 elif cstrtype == 'UniqueConstraint': |
762 elif cstrtype == 'UniqueConstraint': |
763 self.session.pool.source('system').drop_index( |
763 self.session.pool.source('system').drop_index( |
764 self.session, table, column, unique=True) |
764 self.session, table, column, unique=True) |
765 |
765 |
766 def commit_event(self): |
766 def commit_event(self): |
767 self.constraints.remove(self.cstr) |
767 self.constraints.remove(self.cstr) |
768 |
768 |
769 |
769 |
770 def before_delete_constrained_by(session, fromeid, rtype, toeid): |
770 def before_delete_constrained_by(session, fromeid, rtype, toeid): |
782 |
782 |
783 def after_add_constrained_by(session, fromeid, rtype, toeid): |
783 def after_add_constrained_by(session, fromeid, rtype, toeid): |
784 if fromeid in session.query_data('neweids', ()): |
784 if fromeid in session.query_data('neweids', ()): |
785 session.add_query_data(fromeid, toeid) |
785 session.add_query_data(fromeid, toeid) |
786 |
786 |
787 |
787 |
788 # schema permissions synchronization ########################################## |
788 # schema permissions synchronization ########################################## |
789 |
789 |
790 class PermissionOp(Operation): |
790 class PermissionOp(Operation): |
791 """base class to synchronize schema permission definitions""" |
791 """base class to synchronize schema permission definitions""" |
792 def __init__(self, session, perm, etype_eid): |
792 def __init__(self, session, perm, etype_eid): |
803 """synchronize schema when a *_permission relation has been added on a group |
803 """synchronize schema when a *_permission relation has been added on a group |
804 """ |
804 """ |
805 def __init__(self, session, perm, etype_eid, group_eid): |
805 def __init__(self, session, perm, etype_eid, group_eid): |
806 self.group = entity_name(session, group_eid) |
806 self.group = entity_name(session, group_eid) |
807 PermissionOp.__init__(self, session, perm, etype_eid) |
807 PermissionOp.__init__(self, session, perm, etype_eid) |
808 |
808 |
809 def commit_event(self): |
809 def commit_event(self): |
810 """the observed connections pool has been commited""" |
810 """the observed connections pool has been commited""" |
811 try: |
811 try: |
812 erschema = self.schema[self.name] |
812 erschema = self.schema[self.name] |
813 except KeyError: |
813 except KeyError: |
814 # duh, schema not found, log error and skip operation |
814 # duh, schema not found, log error and skip operation |
815 self.error('no schema for %s', self.name) |
815 self.error('no schema for %s', self.name) |
816 return |
816 return |
817 groups = list(erschema.get_groups(self.perm)) |
817 groups = list(erschema.get_groups(self.perm)) |
818 try: |
818 try: |
819 groups.index(self.group) |
819 groups.index(self.group) |
820 self.warning('group %s already have permission %s on %s', |
820 self.warning('group %s already have permission %s on %s', |
821 self.group, self.perm, erschema.type) |
821 self.group, self.perm, erschema.type) |
822 except ValueError: |
822 except ValueError: |
823 groups.append(self.group) |
823 groups.append(self.group) |
828 expression |
828 expression |
829 """ |
829 """ |
830 def __init__(self, session, perm, etype_eid, expression): |
830 def __init__(self, session, perm, etype_eid, expression): |
831 self.expr = expression |
831 self.expr = expression |
832 PermissionOp.__init__(self, session, perm, etype_eid) |
832 PermissionOp.__init__(self, session, perm, etype_eid) |
833 |
833 |
834 def commit_event(self): |
834 def commit_event(self): |
835 """the observed connections pool has been commited""" |
835 """the observed connections pool has been commited""" |
836 try: |
836 try: |
837 erschema = self.schema[self.name] |
837 erschema = self.schema[self.name] |
838 except KeyError: |
838 except KeyError: |
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) |
855 |
855 |
856 |
856 |
857 |
857 |
858 class DelGroupPermissionOp(AddGroupPermissionOp): |
858 class DelGroupPermissionOp(AddGroupPermissionOp): |
859 """synchronize schema when a *_permission relation has been deleted from a group""" |
859 """synchronize schema when a *_permission relation has been deleted from a group""" |
860 |
860 |
861 def commit_event(self): |
861 def commit_event(self): |
862 """the observed connections pool has been commited""" |
862 """the observed connections pool has been commited""" |
863 try: |
863 try: |
864 erschema = self.schema[self.name] |
864 erschema = self.schema[self.name] |
865 except KeyError: |
865 except KeyError: |
866 # duh, schema not found, log error and skip operation |
866 # duh, schema not found, log error and skip operation |
867 self.error('no schema for %s', self.name) |
867 self.error('no schema for %s', self.name) |
868 return |
868 return |
869 groups = list(erschema.get_groups(self.perm)) |
869 groups = list(erschema.get_groups(self.perm)) |
870 try: |
870 try: |
871 groups.remove(self.group) |
871 groups.remove(self.group) |
872 erschema.set_groups(self.perm, groups) |
872 erschema.set_groups(self.perm, groups) |
873 except ValueError: |
873 except ValueError: |
874 self.error('can\'t remove permission %s on %s to group %s', |
874 self.error('can\'t remove permission %s on %s to group %s', |
875 self.perm, erschema.type, self.group) |
875 self.perm, erschema.type, self.group) |
876 |
876 |
877 |
877 |
878 class DelRQLExpressionPermissionOp(AddRQLExpressionPermissionOp): |
878 class DelRQLExpressionPermissionOp(AddRQLExpressionPermissionOp): |
879 """synchronize schema when a *_permission relation has been deleted from an rql expression""" |
879 """synchronize schema when a *_permission relation has been deleted from an rql expression""" |
880 |
880 |
881 def commit_event(self): |
881 def commit_event(self): |
882 """the observed connections pool has been commited""" |
882 """the observed connections pool has been commited""" |
883 try: |
883 try: |
884 erschema = self.schema[self.name] |
884 erschema = self.schema[self.name] |
885 except KeyError: |
885 except KeyError: |
895 self.error('can\'t remove permission %s on %s for expression %s', |
895 self.error('can\'t remove permission %s on %s for expression %s', |
896 self.perm, erschema.type, self.expr) |
896 self.perm, erschema.type, self.expr) |
897 return |
897 return |
898 erschema.set_rqlexprs(self.perm, rqlexprs) |
898 erschema.set_rqlexprs(self.perm, rqlexprs) |
899 |
899 |
900 |
900 |
901 def before_del_permission(session, subject, rtype, object): |
901 def before_del_permission(session, subject, rtype, object): |
902 """delete entity/relation *_permission, need to update schema |
902 """delete entity/relation *_permission, need to update schema |
903 |
903 |
904 skip the operation if the related type is being deleted |
904 skip the operation if the related type is being deleted |
905 """ |
905 """ |
941 hm.register_hook(before_del_eetype, 'before_delete_entity', 'CWEType') |
941 hm.register_hook(before_del_eetype, 'before_delete_entity', 'CWEType') |
942 hm.register_hook(after_del_eetype, 'after_delete_entity', 'CWEType') |
942 hm.register_hook(after_del_eetype, 'after_delete_entity', 'CWEType') |
943 hm.register_hook(before_del_ertype, 'before_delete_entity', 'CWRType') |
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', 'CWConstraint') |
948 hm.register_hook(after_add_econstraint, 'after_add_entity', 'CWConstraint') |
949 hm.register_hook(after_update_econstraint, 'after_update_entity', 'CWConstraint') |
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') |