tachybase_todo/packages/plugins/collection-manager/src/migrations/20221121111113-update-id-to-bigint.ts
ChengLei Shao 89c1d39b6b
Fix/pg schema with inherit (#1446)
* fix: pg schema in sync runner

* fix: restore with pg schema

* chore: databse utils

* fix: test

* Update nocobase-test.yml

* fix: test

* chore: db schema test options

* feat: collection add schema table name method

* fix: update to bigint test

* fix: schema test

* chore: show constraints query path

* chore: show constraints query path

* Update nocobase-test.yml

* chore: test

* fix: test

* fix: test

* fix: test table name

* fix: inherited collection test
2023-02-16 10:53:04 +08:00

195 lines
5.5 KiB
TypeScript

import { DataTypes } from '@nocobase/database';
import { Migration } from '@nocobase/server';
export default class UpdateIdToBigIntMigrator extends Migration {
async up() {
const result = await this.app.version.satisfies('<0.9.0-alpha.1');
if (!result) {
return;
}
const db = this.app.db;
await db.getCollection('fields').repository.update({
filter: {
$or: [
{
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) => {
const columnName = model.rawAttributes[fieldName].field;
let sql;
const tableName = model.tableName;
const addSchemaTableName = db.utils.addSchema(tableName);
const quoteTableName = db.utils.quoteTable(addSchemaTableName);
const collection = db.modelCollection.get(model);
const fieldRecord = await db.getCollection('fields').repository.findOne({
filter: {
collectionName: collection.name,
name: fieldName,
type: 'integer',
},
});
if (fieldRecord) {
fieldRecord.set('type', 'bigInt');
await fieldRecord.save();
}
if (model.rawAttributes[fieldName].type instanceof DataTypes.INTEGER) {
if (db.inDialect('postgres')) {
sql = `ALTER TABLE ${quoteTableName} ALTER COLUMN "${columnName}" 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(addSchemaTableName, query);
sql = sql.replace(' PRIMARY KEY;', ' ;');
}
try {
await this.sequelize.query(sql, {});
} catch (err) {
if (err.message.includes('does not exist') || err.message.includes('cannot alter inherited column')) {
return;
}
throw err;
}
if (db.inDialect('postgres')) {
const sequenceQuery = `SELECT pg_get_serial_sequence('${quoteTableName}', '${columnName}');`;
const [result] = await this.sequelize.query(sequenceQuery, {});
const sequenceName = result[0]['pg_get_serial_sequence'];
if (sequenceName) {
await this.sequelize.query(`ALTER SEQUENCE ${sequenceName} AS BIGINT;`, {});
}
}
this.app.log.info(`updated ${tableName}.${fieldName} to BIGINT`, tableName, fieldName);
}
};
const singleForeignFields = await db.getCollection('fields').repository.find({
filter: {
options: {
isForeignKey: true,
},
type: 'integer',
},
});
for (const field of singleForeignFields) {
const collection = db.getCollection(field.get('collectionName'));
if (!collection) {
console.log('collection not found', field.get('collectionName'));
}
await updateToBigInt(collection.model, field.get('name'));
}
//@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) {
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;
}
}
}
}