diff --git a/packages/plugins/collection-manager/src/migrations/20221121111110-update-fk-type.ts b/packages/plugins/collection-manager/src/migrations/20221121111110-update-fk-type.ts new file mode 100644 index 000000000..39a299a73 --- /dev/null +++ b/packages/plugins/collection-manager/src/migrations/20221121111110-update-fk-type.ts @@ -0,0 +1,145 @@ +import { DataTypes } from '@nocobase/database'; +import { Migration } from '@nocobase/server'; + +export default class UpdateIdToBigIntMigrator extends Migration { + async up() { + const db = this.app.db; + + await db.getCollection('fields').repository.update({ + filter: { + name: 'id', + type: 'integer', + }, + values: { + type: 'bigInt', + }, + }); + + if (!db.inDialect('mysql', 'postgres')) { + return; + } + + const models = []; + + const queryInterface = db.sequelize.getQueryInterface() as any; + + const queryGenerator = queryInterface.queryGenerator as any; + + const updateToBigInt = async (model, fieldName) => { + let sql; + + const tableName = model.tableName; + if (model.rawAttributes[fieldName].type instanceof DataTypes.INTEGER) { + if (db.inDialect('postgres')) { + sql = `ALTER TABLE "${tableName}" ALTER COLUMN "${fieldName}" SET DATA TYPE BIGINT;`; + } else if (db.inDialect('mysql')) { + const dataTypeOrOptions = model.rawAttributes[fieldName]; + const attributeName = fieldName; + + const query = queryGenerator.attributesToSQL( + { + [attributeName]: queryInterface.normalizeAttribute({ + ...dataTypeOrOptions, + type: DataTypes.BIGINT, + }), + }, + { + context: 'changeColumn', + table: tableName, + }, + ); + sql = queryGenerator.changeColumnQuery(tableName, query); + + sql = sql.replace(' PRIMARY KEY;', ' ;'); + } + + try { + await this.sequelize.query(sql, {}); + } catch (err) { + if (err.message.includes('does not exist')) { + return; + } + throw err; + } + + this.app.log.info(`updated ${tableName}.${fieldName} to BIGINT`, tableName, fieldName); + } + }; + + //@ts-ignore + this.app.db.sequelize.modelManager.forEachModel((model) => { + models.push(model); + }); + + for (const model of models) { + try { + const primaryKeyField = model.tableAttributes[model.primaryKeyField]; + + const collection = db.modelCollection.get(model); + + if (!collection) { + continue; + } + + if (primaryKeyField && primaryKeyField.primaryKey) { + await updateToBigInt(model, model.primaryKeyField); + } + + if (model.tableAttributes['sort'] && model.tableAttributes['sort'].type instanceof DataTypes.INTEGER) { + await updateToBigInt(model, 'sort'); + } + + const associations = model.associations; + for (const associationName of Object.keys(associations)) { + const association = associations[associationName]; + + const type = association.associationType; + let foreignModel; + let fieldName; + + if (type === 'BelongsTo') { + foreignModel = association.source; + fieldName = association.foreignKey; + } + + if (type === 'HasMany') { + foreignModel = association.target; + fieldName = association.foreignKey; + } + + if (type === 'HasOne') { + foreignModel = association.target; + fieldName = association.foreignKey; + } + + if (foreignModel && fieldName) { + const cf = await db.getRepository('collections.fields', foreignModel.name).findOne({ + filterByTk: fieldName, + }); + if (cf) { + cf.interface = 'integer'; + cf.type = 'bigInt'; + await cf.save(); + } + await updateToBigInt(foreignModel, fieldName); + } + + if (type === 'BelongsToMany') { + const throughModel = association.through.model; + const otherKey = association.otherKey; + const foreignKey = association.foreignKey; + + await updateToBigInt(throughModel, otherKey); + await updateToBigInt(throughModel, foreignKey); + } + } + } catch (error) { + if (error.message.includes('cannot alter inherited column')) { + continue; + } + + throw error; + } + } + } +}