mirror of
https://github.com/yweber/lodel2.git
synced 2026-07-05 07:10:48 +02:00
New version of fieldtypes class hierarchy + partial migrationhandler adaptation
This commit is contained in:
parent
5acf09a1ee
commit
bbf33373d0
10 changed files with 240 additions and 168 deletions
80
DataSource/MySQL/fieldtypes.py
Normal file
80
DataSource/MySQL/fieldtypes.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#-*- coding: utf-8 -*-
|
||||
|
||||
## @package DataSource.MySQL.fieldtypes
|
||||
#
|
||||
# Defines usefull function to handle fieldtype in MySQL datasource
|
||||
|
||||
import EditorialModel
|
||||
from EditorialModel import fieldtypes as ed_fieldtypes
|
||||
import EditorialModel.fieldtypes.generic
|
||||
from EditorialModel.fieldtypes.generic import SingleValueFieldType, MultiValueFieldType, ReferenceFieldType
|
||||
import EditorialModel.fieldtypes.integer
|
||||
import EditorialModel.fieldtypes.char
|
||||
import EditorialModel.fieldtypes.bool
|
||||
import EditorialModel.fieldtypes.text
|
||||
import EditorialModel.fieldtypes.rel2type
|
||||
|
||||
## @brief Returns column specs from fieldtype
|
||||
# @param emfieldtype EmFieldType : An EmFieldType insance
|
||||
# @todo escape default value
|
||||
def singlevaluefieldtype_db_init_specs(emfieldtype):
|
||||
colspec = ''
|
||||
if not emfieldtype.nullable:
|
||||
colspec = 'NOT NULL'
|
||||
if hasattr(emfieldtype, 'default'):
|
||||
colspec += ' DEFAULT '
|
||||
if emfieldtype.default is None:
|
||||
colspec += 'NULL '
|
||||
else:
|
||||
colspec += emfieldtype.default # ESCAPE VALUE HERE !!!!
|
||||
|
||||
if emfieldtype.name == 'pk':
|
||||
colspec += ' AUTO_INCREMENT'
|
||||
|
||||
return colspec
|
||||
|
||||
## @brief Given a fieldtype return instructions to be executed by the migration handler
|
||||
#
|
||||
# The returned value in a tuple of len = 3
|
||||
#
|
||||
# The first items gives instruction type. Possible values are :
|
||||
# - 'column' : add a column
|
||||
# - the second tuple item is the SQL type of the new column
|
||||
# - the third tuple item is the SQL specs (constraints like default value, nullable, unique , auto_increment etc.)
|
||||
# - 'table' : add a column in another table and make a fk to the current table
|
||||
# - the second tuple item is a tuple(key_name, key_value)
|
||||
# - the third tuple item is a tuple(column_type, column_spec)
|
||||
# @param fieldtype GenericFieldType : A FieldType instance
|
||||
# @return a tuple (instruction_type, infos)
|
||||
def fieldtype_db_init(fieldtype):
|
||||
if isinstance(fieldtype, EditorialModel.fieldtypes.rel2type.EmFieldType):
|
||||
return (None, None, None)
|
||||
elif isinstance(fieldtype, SingleValueFieldType):
|
||||
res = [ 'column', None, singlevaluefieldtype_db_init_specs(fieldtype) ]
|
||||
# We will create a column
|
||||
if isinstance(fieldtype, EditorialModel.fieldtypes.integer.EmFieldType):
|
||||
res[1] = 'INT'
|
||||
elif isinstance(fieldtype, EditorialModel.fieldtypes.char.EmFieldType):
|
||||
res[1] = 'VARCHAR(%d)' % fieldtype.max_length
|
||||
elif isinstance(fieldtype, EditorialModel.fieldtypes.text.EmFieldType):
|
||||
res[1] = 'TEXT'
|
||||
elif isinstance(fieldtype, EditorialModel.fieldtypes.bool.EmFieldType):
|
||||
res[1] = 'BOOL'
|
||||
elif isinstance(fieldtype, EditorialModel.fieldtypes.datetime.EmFieldType):
|
||||
res[1] = 'DATETIME'
|
||||
elif isinstance(fieldtype, EditorialModel.fieldtypes.generic.ReferenceFieldType):
|
||||
res[1] = 'INT'
|
||||
else:
|
||||
raise RuntimeError("Unsupported fieldtype : ", fieldtype)
|
||||
return tuple(res)
|
||||
elif isinstance(fieldtype, MultiValueFieldType):
|
||||
res = [ 'table', None, None ]
|
||||
key_type, _ = fieldtype_db_init(fieldtype.key_fieldtype)
|
||||
key_name = fieldtype.keyname
|
||||
res[1] = (key_name, key_type)
|
||||
res[2] = fieldtype_db_init(fieldtype.value_fieldtype)
|
||||
else:
|
||||
raise NotImplementedError("Not yet implemented")
|
||||
|
||||
|
||||
|
||||
|
|
@ -10,6 +10,7 @@ import EditorialModel.classtypes
|
|||
import EditorialModel.fieldtypes
|
||||
import EditorialModel.fieldtypes.generic
|
||||
|
||||
from DataSource.MySQL import fieldtypes as fieldtypes_utils
|
||||
from DataSource.MySQL import utils
|
||||
from DataSource.dummy.migrationhandler import DummyMigrationHandler
|
||||
|
||||
|
|
@ -274,8 +275,6 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
|||
|
||||
# Creating foreign keys between relation and object table
|
||||
sup_cname, sub_cname = self.get_sup_and_sub_cols()
|
||||
self._add_fk(tname, object_tname, sup_cname, self._object_pk[0], 'fk_relations_superiors')
|
||||
self._add_fk(tname, object_tname, sub_cname, self._object_pk[0], 'fk_relations_subordinate')
|
||||
|
||||
## @brief Returns the fieldname for superior and subordinate in relation table
|
||||
# @return a tuple (superior_name, subordinate_name)
|
||||
|
|
@ -301,8 +300,9 @@ class MysqlMigrationHandler(DummyMigrationHandler):
|
|||
def _create_table(self, table_name, pk_name, pk_ftype, engine, charset='utf8', if_exists='nothing'):
|
||||
#Escaped table name
|
||||
etname = utils.escape_idname(table_name)
|
||||
pk_type = self._field_to_type(pk_ftype)
|
||||
pk_specs = self._field_to_specs(pk_ftype)
|
||||
instr_type, pk_type, pk_specs = fieldtypes_utils.fieldtype_db_init(pk_ftype)
|
||||
if instr_type != 'column':
|
||||
raise ValueError("Migration handler doesn't support MultiValueFieldType as primary keys")
|
||||
|
||||
if if_exists == 'drop':
|
||||
self._query("""DROP TABLE IF EXISTS {table_name};""".format(table_name=etname))
|
||||
|
|
@ -343,11 +343,17 @@ ADD COLUMN {col_name} {col_type} {col_specs};"""
|
|||
etname = utils.escape_idname(table_name)
|
||||
ecname = utils.escape_idname(col_name)
|
||||
|
||||
instr, col_type, col_specs = fieldtypes_utils.fieldtype_db_init(col_fieldtype)
|
||||
if instr is None:
|
||||
return True
|
||||
if instr != "column":
|
||||
raise RuntimeError("Bad implementation")
|
||||
|
||||
add_col = add_col.format(
|
||||
table_name=etname,
|
||||
col_name=ecname,
|
||||
col_type=self._field_to_type(col_fieldtype),
|
||||
col_specs=self._field_to_specs(col_fieldtype),
|
||||
col_type=col_type,
|
||||
col_specs=col_specs,
|
||||
)
|
||||
try:
|
||||
self._query(add_col)
|
||||
|
|
@ -359,6 +365,31 @@ ADD COLUMN {col_name} {col_type} {col_specs};"""
|
|||
#LOG
|
||||
print("Aborded, column `%s` exists" % col_name)
|
||||
return False
|
||||
|
||||
if isinstance(col_fieldtype, EditorialModel.fieldtypes.generic.ReferenceFieldType):
|
||||
# We have to create a FK !
|
||||
if col_fieldtype.reference == 'object':
|
||||
dst_table_name = utils.common_tables['object']
|
||||
dst_col_name, _ = self._object_pk
|
||||
elif col_fieldtypes.reference == 'relation':
|
||||
dst_table_name = utils.common_tables['relation']
|
||||
dst_col_name, _ = self._relation_pk
|
||||
|
||||
fk_name = 'fk_%s-%s_%s-%s' % (
|
||||
table_name,
|
||||
col_name,
|
||||
dst_table_name,
|
||||
dst_col_name,
|
||||
)
|
||||
|
||||
self._add_fk(
|
||||
src_table_name = table_name,
|
||||
dst_table_name = dst_table_name,
|
||||
src_col_name = col_name,
|
||||
dst_col_name = dst_col_name,
|
||||
fk_name = fk_name
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
## @brief Add a foreign key
|
||||
|
|
@ -398,7 +429,7 @@ FOREIGN KEY ({src_col}) references {dst_table}({dst_col});""".format(
|
|||
self._query("""ALTER TABLE {src_table}
|
||||
DROP FOREIGN KEY {fk_name}""".format(
|
||||
src_table=utils.escape_idname(src_table_name),
|
||||
fk_name=fk_name
|
||||
fk_name=utils.escape_idname(fk_name)
|
||||
))
|
||||
except self._dbmodule.err.InternalError:
|
||||
# If the FK don't exists we do not care
|
||||
|
|
@ -412,7 +443,7 @@ DROP FOREIGN KEY {fk_name}""".format(
|
|||
colval_l_ins = dict() # param for insert trigger
|
||||
|
||||
for cname, cftype in cols_ftype.items():
|
||||
if cftype.ftype == 'datetime':
|
||||
if isinstance(cftype, EditorialModel.fieldtypes.datetime.EmFieldType):
|
||||
if cftype.now_on_update:
|
||||
colval_l_upd[cname] = 'NOW()'
|
||||
if cftype.now_on_create:
|
||||
|
|
@ -450,65 +481,6 @@ FOR EACH ROW SET {col_val_list};""".format(
|
|||
)
|
||||
self._query(trig_q)
|
||||
|
||||
## @brief Identifier escaping
|
||||
# @param idname str : An SQL identifier
|
||||
#def _idname_escape(self, idname):
|
||||
# if '`' in idname:
|
||||
# raise ValueError("Invalid name : '%s'"%idname)
|
||||
# return '`%s`'%idname
|
||||
|
||||
## @brief Returns column specs from fieldtype
|
||||
# @param emfieldtype EmFieldType : An EmFieldType insance
|
||||
# @todo escape default value
|
||||
def _field_to_specs(self, emfieldtype):
|
||||
colspec = ''
|
||||
if not emfieldtype.nullable:
|
||||
colspec = 'NOT NULL'
|
||||
if hasattr(emfieldtype, 'default'):
|
||||
colspec += ' DEFAULT '
|
||||
if emfieldtype.default is None:
|
||||
colspec += 'NULL '
|
||||
else:
|
||||
colspec += emfieldtype.default # ESCAPE VALUE HERE !!!!
|
||||
|
||||
if emfieldtype.name == 'pk':
|
||||
colspec += ' AUTO_INCREMENT'
|
||||
|
||||
return colspec
|
||||
|
||||
## @brief Given a fieldtype return a MySQL type specifier
|
||||
# @param emfieldtype EmFieldType : A fieldtype
|
||||
# @return the corresponding MySQL type
|
||||
def _field_to_type(self, emfieldtype):
|
||||
ftype = emfieldtype.ftype
|
||||
|
||||
if ftype == 'char' or ftype == 'str':
|
||||
res = "VARCHAR(%d)" % emfieldtype.max_length
|
||||
elif ftype == 'text':
|
||||
res = "TEXT"
|
||||
elif ftype == 'datetime':
|
||||
res = "DATETIME"
|
||||
# client side workaround for only one column with CURRENT_TIMESTAMP : giving NULL to timestamp that don't allows NULL
|
||||
# cf. https://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html#idm139961275230400
|
||||
# The solution for the migration handler is to create triggers :
|
||||
# CREATE TRIGGER trigger_name BEFORE INSERT ON `my_super_table`
|
||||
# FOR EACH ROW SET NEW.my_date_column = NOW();
|
||||
# and
|
||||
# CREATE TRIGGER trigger_name BEFORE UPDATE ON
|
||||
|
||||
elif ftype == 'bool':
|
||||
res = "BOOL"
|
||||
elif ftype == 'int':
|
||||
res = "INT"
|
||||
elif ftype == 'rel2type':
|
||||
res = "INT"
|
||||
elif ftype == 'leobject':
|
||||
res = "INT"
|
||||
else:
|
||||
raise ValueError("Unsuported fieldtype ftype : %s" % ftype)
|
||||
|
||||
return res
|
||||
|
||||
## @brief Delete all table created by the MH
|
||||
# @param model Model : the Editorial model
|
||||
def __purge_db(self, model):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue