|
@@ -42,27 +42,22 @@ from DataSource.dummy.migrationhandler import DummyMigrationHandler
|
42
|
42
|
class MysqlMigrationHandler(DummyMigrationHandler):
|
43
|
43
|
|
44
|
44
|
## @brief Construct a MysqlMigrationHandler
|
45
|
|
- # @param host str : The db host
|
46
|
|
- # @param user str : The db user
|
47
|
|
- # @param password str : The db password
|
48
|
|
- # @param db str : The db name
|
49
|
|
- def __init__(self, module = pymysql, conn_args = None, db_engine='InnoDB', foreign_keys=True, debug=None, dryrun=False, drop_if_exists=False):
|
50
|
|
-
|
|
45
|
+ # @param module : sql module
|
|
46
|
+ # @param conn_args dict : A dict containing connection options
|
|
47
|
+ def __init__(self, module=pymysql, conn_args=None, db_engine='InnoDB', foreign_keys=True, debug=None, dryrun=False, drop_if_exists=False):
|
51
|
48
|
# Database connection
|
52
|
49
|
self._dbmodule = module
|
53
|
50
|
if conn_args is None:
|
54
|
51
|
conn_args = copy.copy(Settings.get('datasource')['default'])
|
55
|
52
|
self._dbmodule = conn_args['module']
|
56
|
53
|
del conn_args['module']
|
57
|
|
- self.db = self._dbmodule.connect(**conn_args)
|
58
|
|
-
|
|
54
|
+ self.db_conn = self._dbmodule.connect(**conn_args)
|
59
|
55
|
# Fetch options
|
60
|
56
|
self.debug = Settings.get('debug') if debug is None else debug
|
61
|
57
|
self.dryrun = dryrun
|
62
|
58
|
self.db_engine = db_engine
|
63
|
59
|
self.foreign_keys = foreign_keys if db_engine == 'InnoDB' else False
|
64
|
60
|
self.drop_if_exists = drop_if_exists
|
65
|
|
-
|
66
|
61
|
#Create default tables
|
67
|
62
|
self._create_default_tables(self.drop_if_exists)
|
68
|
63
|
|
|
@@ -93,7 +88,7 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
93
|
88
|
elif new_state is None:
|
94
|
89
|
#non relationnal EmField deletion
|
95
|
90
|
if emfield.name not in EditorialModel.classtypes.common_fields.keys():
|
96
|
|
- self.del_col_from_emfield(em, uid)
|
|
91
|
+ self.delete_col_from_emfield(em, uid)
|
97
|
92
|
else:
|
98
|
93
|
#relationnal field
|
99
|
94
|
if initial_state is None:
|
|
@@ -113,13 +108,13 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
113
|
108
|
# @note this function handles the table creation
|
114
|
109
|
# @param em Model : EditorialModel.model.Model instance
|
115
|
110
|
# @param rfuid int : Relationnal field uid
|
116
|
|
- def add_relationnal_field(self, em, rfuid):
|
117
|
|
- emfield = em.component(rfuid)
|
|
111
|
+ def add_relationnal_field(self, edmod, rfuid):
|
|
112
|
+ emfield = edmod.component(rfuid)
|
118
|
113
|
if not isinstance(emfield, EditorialModel.fields.EmField):
|
119
|
114
|
raise ValueError("The given uid is not an EmField uid")
|
120
|
115
|
|
121
|
|
- r2tf = em.component(emfield.rel_field_id)
|
122
|
|
- tname = self._r2t2table_name(em, r2tf)
|
|
116
|
+ r2tf = edmod.component(emfield.rel_field_id)
|
|
117
|
+ tname = self._r2t2table_name(edmod, r2tf)
|
123
|
118
|
pkname, pkftype = self._relation_pk
|
124
|
119
|
|
125
|
120
|
#If not exists create a relational table
|
|
@@ -130,7 +125,7 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
130
|
125
|
#Add the column
|
131
|
126
|
self._add_column(tname, emfield.name, emfield.fieldtype_instance())
|
132
|
127
|
#Update table triggers
|
133
|
|
- self._generate_triggers(tname, self._r2type2cols(em, r2tf))
|
|
128
|
+ self._generate_triggers(tname, self._r2type2cols(edmod, r2tf))
|
134
|
129
|
|
135
|
130
|
## @brief Delete a rel2type attribute
|
136
|
131
|
#
|
|
@@ -138,27 +133,27 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
138
|
133
|
# @note this method handles the table deletion
|
139
|
134
|
# @param em Model : EditorialModel.model.Model instance
|
140
|
135
|
# @param rfuid int : Relationnal field uid
|
141
|
|
- def del_relationnal_field(self, em, rfuid):
|
142
|
|
- emfield = em.component(rfuid)
|
|
136
|
+ def del_relationnal_field(self, edmod, rfuid):
|
|
137
|
+ emfield = edmod.component(rfuid)
|
143
|
138
|
if not isinstance(emfield, EditorialModel.fields.EmField):
|
144
|
139
|
raise ValueError("The given uid is not an EmField uid")
|
145
|
140
|
|
146
|
|
- r2tf = em.component(emfield.rel_field_id)
|
147
|
|
- tname = self._r2t2table_name(em, r2tf)
|
|
141
|
+ r2tf = edmod.component(emfield.rel_field_id)
|
|
142
|
+ tname = self._r2t2table_name(edmod, r2tf)
|
148
|
143
|
|
149
|
|
- if len(self._r2type2cols(em, r2tf)) == 1:
|
|
144
|
+ if len(self._r2type2cols(edmod, r2tf)) == 1:
|
150
|
145
|
#The table can be deleted (no more attribute for this rel2type)
|
151
|
146
|
self._query("""DROP TABLE {table_name}""".format(table_name=tname))
|
152
|
147
|
else:
|
153
|
148
|
self._del_column(tname, emfield.name)
|
154
|
149
|
#Update table triggers
|
155
|
|
- self._generate_triggers(tname, self._r2type2cols(em, r2tf))
|
|
150
|
+ self._generate_triggers(tname, self._r2type2cols(edmod, r2tf))
|
156
|
151
|
|
157
|
152
|
## @brief Given an EmField uid add a column to the corresponding table
|
158
|
153
|
# @param em Model : A Model instance
|
159
|
154
|
# @param uid int : An EmField uid
|
160
|
|
- def add_col_from_emfield(self, em, uid):
|
161
|
|
- emfield = em.component(uid)
|
|
155
|
+ def add_col_from_emfield(self, edmod, uid):
|
|
156
|
+ emfield = edmod.component(uid)
|
162
|
157
|
if not isinstance(emfield, EditorialModel.fields.EmField):
|
163
|
158
|
raise ValueError("The given uid is not an EmField uid")
|
164
|
159
|
|
|
@@ -172,8 +167,8 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
172
|
167
|
## @brief Given a class uid create the coressponding table
|
173
|
168
|
# @param em Model : A Model instance
|
174
|
169
|
# @param uid int : An EmField uid
|
175
|
|
- def create_emclass_table(self, em, uid, engine):
|
176
|
|
- emclass = em.component(uid)
|
|
170
|
+ def create_emclass_table(self, edmod, uid, engine):
|
|
171
|
+ emclass = edmod.component(uid)
|
177
|
172
|
if not isinstance(emclass, EditorialModel.classes.EmClass):
|
178
|
173
|
raise ValueError("The given uid is not an EmClass uid")
|
179
|
174
|
pkname, pktype = self._object_pk
|
|
@@ -187,8 +182,8 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
187
|
182
|
## @brief Given an EmClass uid delete the corresponding table
|
188
|
183
|
# @param em Model : A Model instance
|
189
|
184
|
# @param uid int : An EmField uid
|
190
|
|
- def delete_emclass_table(self, em, uid):
|
191
|
|
- emclass = em.component(uid)
|
|
185
|
+ def delete_emclass_table(self, edmod, uid):
|
|
186
|
+ emclass = edmod.component(uid)
|
192
|
187
|
if not isinstance(emclass, EditorialModel.classes.EmClass):
|
193
|
188
|
raise ValueError("The give uid is not an EmClass uid")
|
194
|
189
|
tname = utils.object_table_name(emclass.name)
|
|
@@ -202,8 +197,8 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
202
|
197
|
## @brief Given an EmField delete the corresponding column
|
203
|
198
|
# @param em Model : an @ref EditorialModel.model.Model instance
|
204
|
199
|
# @param uid int : an EmField uid
|
205
|
|
- def delete_col_from_emfield(self, em, uid):
|
206
|
|
- emfield = em.component(uid)
|
|
200
|
+ def delete_col_from_emfield(self, edmod, uid):
|
|
201
|
+ emfield = edmod.component(uid)
|
207
|
202
|
if not isinstance(emfield, EditorialModel.fields.EmField):
|
208
|
203
|
raise ValueError("The given uid is not an EmField uid")
|
209
|
204
|
|
|
@@ -214,7 +209,7 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
214
|
209
|
|
215
|
210
|
self._del_column(tname, emfield.name)
|
216
|
211
|
# Refresh the table triggers
|
217
|
|
- cols_ls = self._class2cols(emclass)
|
|
212
|
+ cols_l = self._class2cols(emclass)
|
218
|
213
|
self._generate_triggers(tname, cols_l)
|
219
|
214
|
|
220
|
215
|
## @brief Delete a column from a table
|
|
@@ -230,16 +225,16 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
230
|
225
|
# @param em Model : A Model instance
|
231
|
226
|
# @param emfield EmField : An EmField instance
|
232
|
227
|
# @return a table name
|
233
|
|
- def _r2t2table_name(self, em, emfield):
|
|
228
|
+ def _r2t2table_name(self, edmod, emfield):
|
234
|
229
|
emclass = emfield.em_class
|
235
|
|
- emtype = em.component(emfield.rel_to_type_id)
|
|
230
|
+ emtype = edmod.component(emfield.rel_to_type_id)
|
236
|
231
|
return utils.r2t_table_name(emclass.name, emtype.name)
|
237
|
232
|
|
238
|
233
|
## @brief Generate a columns_fieldtype dict given a rel2type EmField
|
239
|
234
|
# @param em Model : an @ref EditorialModel.model.Model instance
|
240
|
235
|
# @param emfield EmField : and @ref EditorialModel.fields.EmField instance
|
241
|
|
- def _r2type2cols(self, em, emfield):
|
242
|
|
- return {f.name: f.fieldtype_instance() for f in em.components('EmField') if f.rel_field_id == emfield.uid}
|
|
236
|
+ def _r2type2cols(self, edmod, emfield):
|
|
237
|
+ return {f.name: f.fieldtype_instance() for f in edmod.components('EmField') if f.rel_field_id == emfield.uid}
|
243
|
238
|
|
244
|
239
|
## @brief Generate a columns_fieldtype dict given an EmClass
|
245
|
240
|
# @param emclass EmClass : An EmClass instance
|
|
@@ -280,7 +275,7 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
280
|
275
|
sup_cname, sub_cname = self.get_sup_and_sub_cols()
|
281
|
276
|
self._add_fk(tname, object_tname, sup_cname, self._object_pk[0], 'fk_relations_superiors')
|
282
|
277
|
self._add_fk(tname, object_tname, sub_cname, self._object_pk[0], 'fk_relations_subordinate')
|
283
|
|
-
|
|
278
|
+
|
284
|
279
|
## @brief Returns the fieldname for superior and subordinate in relation table
|
285
|
280
|
# @return a tuple (superior_name, subordinate_name)
|
286
|
281
|
@classmethod
|
|
@@ -293,7 +288,7 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
293
|
288
|
sup = fname
|
294
|
289
|
else:
|
295
|
290
|
sub = fname
|
296
|
|
- return utils.column_name(sup),utils.column_name(sub)
|
|
291
|
+ return utils.column_name(sup), utils.column_name(sub)
|
297
|
292
|
|
298
|
293
|
## @brief Create a table with primary key
|
299
|
294
|
# @param table_name str : table name
|
|
@@ -355,7 +350,7 @@ ADD COLUMN {col_name} {col_type} {col_specs};"""
|
355
|
350
|
)
|
356
|
351
|
try:
|
357
|
352
|
self._query(add_col)
|
358
|
|
- except self._dbmodule.err.InternalError as e:
|
|
353
|
+ except self._dbmodule.err.InternalError:
|
359
|
354
|
if drop_if_exists:
|
360
|
355
|
self._del_column(table_name, col_name)
|
361
|
356
|
self._add_column(table_name, col_name, col_fieldtype, drop_if_exists)
|
|
@@ -370,7 +365,7 @@ ADD COLUMN {col_name} {col_type} {col_specs};"""
|
370
|
365
|
# @param dst_table_name str : The name of the table the FK will point on
|
371
|
366
|
# @param src_col_name str : The name of the concerned column in the src_table
|
372
|
367
|
# @param dst_col_name str : The name of the concerned column in the dst_table
|
373
|
|
- def _add_fk(self, src_table_name, dst_table_name, src_col_name, dst_col_name, fk_name = None):
|
|
368
|
+ def _add_fk(self, src_table_name, dst_table_name, src_col_name, dst_col_name, fk_name=None):
|
374
|
369
|
stname = utils.escape_idname(src_table_name)
|
375
|
370
|
dtname = utils.escape_idname(dst_table_name)
|
376
|
371
|
scname = utils.escape_idname(src_col_name)
|
|
@@ -384,26 +379,26 @@ ADD COLUMN {col_name} {col_type} {col_specs};"""
|
384
|
379
|
self._query("""ALTER TABLE {src_table}
|
385
|
380
|
ADD CONSTRAINT {fk_name}
|
386
|
381
|
FOREIGN KEY ({src_col}) references {dst_table}({dst_col});""".format(
|
387
|
|
- fk_name=utils.escape_idname(fk_name),
|
388
|
|
- src_table=stname,
|
389
|
|
- src_col=scname,
|
390
|
|
- dst_table=dtname,
|
391
|
|
- dst_col=dcname
|
392
|
|
- ))
|
|
382
|
+ fk_name=utils.escape_idname(fk_name),
|
|
383
|
+ src_table=stname,
|
|
384
|
+ src_col=scname,
|
|
385
|
+ dst_table=dtname,
|
|
386
|
+ dst_col=dcname
|
|
387
|
+))
|
393
|
388
|
|
394
|
389
|
## @brief Given a source and a destination table, delete the corresponding FK
|
395
|
390
|
# @param src_table_name str : The name of the table where the FK is
|
396
|
391
|
# @param dst_table_name str : The name of the table the FK point on
|
397
|
392
|
# @warning fails silently
|
398
|
|
- def _del_fk(self, src_table_name, dst_table_name, fk_name = None):
|
|
393
|
+ def _del_fk(self, src_table_name, dst_table_name, fk_name=None):
|
399
|
394
|
if fk_name is None:
|
400
|
395
|
fk_name = utils.escape_idname(utils.get_fk_name(src_table_name, dst_table_name))
|
401
|
396
|
try:
|
402
|
397
|
self._query("""ALTER TABLE {src_table}
|
403
|
398
|
DROP FOREIGN KEY {fk_name}""".format(
|
404
|
|
- src_table=utils.escape_idname(src_table_name),
|
405
|
|
- fk_name=fk_name
|
406
|
|
- ))
|
|
399
|
+ src_table=utils.escape_idname(src_table_name),
|
|
400
|
+ fk_name=fk_name
|
|
401
|
+))
|
407
|
402
|
except self._dbmodule.err.InternalError:
|
408
|
403
|
# If the FK don't exists we do not care
|
409
|
404
|
pass
|
|
@@ -448,10 +443,10 @@ DROP FOREIGN KEY {fk_name}""".format(
|
448
|
443
|
if len(col_val_l) > 0:
|
449
|
444
|
trig_q = """CREATE TRIGGER {trigger_name} BEFORE {moment} ON {table_name}
|
450
|
445
|
FOR EACH ROW SET {col_val_list};""".format(
|
451
|
|
- trigger_name=trigger_name,
|
452
|
|
- table_name=utils.escape_idname(table_name),
|
453
|
|
- moment=moment, col_val_list=col_val_l
|
454
|
|
- )
|
|
446
|
+ trigger_name=trigger_name,
|
|
447
|
+ table_name=utils.escape_idname(table_name),
|
|
448
|
+ moment=moment, col_val_list=col_val_l
|
|
449
|
+)
|
455
|
450
|
self._query(trig_q)
|
456
|
451
|
|
457
|
452
|
## @brief Identifier escaping
|
|
@@ -519,20 +514,20 @@ FOR EACH ROW SET {col_val_list};""".format(
|
519
|
514
|
for uid in [c.uid for c in model.components('EmClass')]:
|
520
|
515
|
try:
|
521
|
516
|
self.delete_emclass_table(model, uid)
|
522
|
|
- except self._dbmodule.err.InternalError as e:
|
523
|
|
- print(e)
|
|
517
|
+ except self._dbmodule.err.InternalError as expt:
|
|
518
|
+ print(expt)
|
524
|
519
|
|
525
|
|
- for tname in [MySQL.get_r2t2table_name(f.em_class.name, model.component(f.rel_to_type_id).name) for f in model.components('EmField') if f.fieldtype == 'rel2type']:
|
|
520
|
+ for tname in [utils.r2t_table_name(f.em_class.name, model.component(f.rel_to_type_id).name) for f in model.components('EmField') if f.fieldtype == 'rel2type']:
|
526
|
521
|
try:
|
527
|
522
|
self._query("DROP TABLE %s;" % tname)
|
528
|
|
- except self._dbmodule.err.InternalError as e:
|
529
|
|
- print(e)
|
|
523
|
+ except self._dbmodule.err.InternalError as expt:
|
|
524
|
+ print(expt)
|
530
|
525
|
|
531
|
|
- for tname in [MySQL.relations_table_name, MySQL.objects_table_name]:
|
|
526
|
+ for tname in [utils.common_tables['relation'], utils.common_tables['relation']]:
|
532
|
527
|
try:
|
533
|
528
|
self._query("DROP TABLE %s;" % tname)
|
534
|
|
- except self._dbmodule.err.InternalError as e:
|
535
|
|
- print(e)
|
|
529
|
+ except self._dbmodule.err.InternalError as expt:
|
|
530
|
+ print(expt)
|
536
|
531
|
|
537
|
532
|
## @brief Return primary key name & fieldtype for relation or object
|
538
|
533
|
@classmethod
|
|
@@ -543,7 +538,7 @@ FOR EACH ROW SET {col_val_list};""".format(
|
543
|
538
|
finfo_cp = copy.copy(finfo)
|
544
|
539
|
del(finfo_cp['fieldtype'])
|
545
|
540
|
return (utils.column_name(fname), fto(**finfo_cp))
|
546
|
|
- raise RuntimeError("No primary key found in common fields : %s"%common_fields)
|
|
541
|
+ raise RuntimeError("No primary key found in common fields : %s" % common_fields)
|
547
|
542
|
|
548
|
543
|
## @brief Exec a query
|
549
|
544
|
# @param query str : SQL query
|
|
@@ -551,9 +546,9 @@ FOR EACH ROW SET {col_val_list};""".format(
|
551
|
546
|
if self.debug:
|
552
|
547
|
print(query + "\n")
|
553
|
548
|
if not self.dryrun:
|
554
|
|
- with self.db.cursor() as cur:
|
|
549
|
+ with self.db_conn.cursor() as cur:
|
555
|
550
|
cur.execute(query)
|
556
|
|
- self.db.commit() # autocommit
|
|
551
|
+ self.db_conn.commit() # autocommit
|
557
|
552
|
|
558
|
553
|
## @brief Given a common field name return an EmFieldType instance
|
559
|
554
|
# @param cname str : Common field name
|
|
@@ -578,7 +573,6 @@ FOR EACH ROW SET {col_val_list};""".format(
|
578
|
573
|
## @brief Returns a dict { colname:fieldtype } of relation table columns
|
579
|
574
|
@property
|
580
|
575
|
def _relation_cols(self):
|
581
|
|
- from_name = EditorialModel.fieldtypes.generic.GenericFieldType.from_name
|
582
|
576
|
res = dict()
|
583
|
577
|
for fieldname, fieldinfo in EditorialModel.classtypes.relations_common_fields.items():
|
584
|
578
|
finfo = copy.copy(fieldinfo)
|