dbgate-tools
Version:
Auxiliary tools for other DbGate packages.
504 lines (503 loc) • 20.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runAlterOperation = exports.AlterPlan = void 0;
const lodash_1 = __importDefault(require("lodash"));
const diffTools_1 = require("./diffTools");
const database_info_alter_processor_1 = require("./database-info-alter-processor");
const DatabaseAnalyser_1 = require("./DatabaseAnalyser");
class AlterPlan {
constructor(wholeOldDb, wholeNewDb, dialect, opts) {
this.wholeOldDb = wholeOldDb;
this.wholeNewDb = wholeNewDb;
this.dialect = dialect;
this.opts = opts;
this.recreates = {
tables: 0,
constraints: 0,
sqlObjects: 0,
};
this.operations = [];
}
createTable(table) {
this.operations.push({
operationType: 'createTable',
newObject: table,
});
}
dropTable(table) {
this.operations.push({
operationType: 'dropTable',
oldObject: table,
});
}
createSqlObject(obj) {
this.operations.push({
operationType: 'createSqlObject',
newObject: obj,
});
}
dropSqlObject(obj) {
this.operations.push({
operationType: 'dropSqlObject',
oldObject: obj,
});
}
createColumn(column) {
this.operations.push({
operationType: 'createColumn',
newObject: column,
});
}
changeColumn(oldColumn, newColumn) {
this.operations.push({
operationType: 'changeColumn',
oldObject: oldColumn,
newObject: newColumn,
});
}
dropColumn(column) {
this.operations.push({
operationType: 'dropColumn',
oldObject: column,
});
}
createConstraint(constraint) {
this.operations.push({
operationType: 'createConstraint',
newObject: constraint,
});
}
changeConstraint(oldConstraint, newConstraint) {
this.operations.push({
operationType: 'changeConstraint',
oldObject: oldConstraint,
newObject: newConstraint,
});
}
dropConstraint(constraint) {
this.operations.push({
operationType: 'dropConstraint',
oldObject: constraint,
});
}
renameTable(table, newName) {
this.operations.push({
operationType: 'renameTable',
object: table,
newName,
});
}
renameSqlObject(table, newName) {
this.operations.push({
operationType: 'renameSqlObject',
object: table,
newName,
});
}
renameColumn(column, newName) {
this.operations.push({
operationType: 'renameColumn',
object: column,
newName,
});
}
renameConstraint(constraint, newName) {
this.operations.push({
operationType: 'renameConstraint',
object: constraint,
newName,
});
}
recreateTable(table, operations) {
this.operations.push({
operationType: 'recreateTable',
table,
operations,
});
this.recreates.tables += 1;
}
fillPreloadedRows(table, oldRows, newRows, key, insertOnly, autoIncrementColumn) {
this.operations.push({
operationType: 'fillPreloadedRows',
table,
oldRows,
newRows,
key,
insertOnly,
autoIncrementColumn,
});
}
setTableOption(table, optionName, optionValue) {
this.operations.push({
operationType: 'setTableOption',
table,
optionName,
optionValue,
});
}
run(processor) {
for (const op of this.operations) {
runAlterOperation(op, processor);
}
}
_hasOnlyCommentChange(op) {
if (op.operationType === 'changeColumn') {
return lodash_1.default.isEqual(lodash_1.default.omit(op.oldObject, ['columnComment', 'ordinal']), lodash_1.default.omit(op.newObject, ['columnComment', 'ordinal']));
}
return false;
}
_getDependendColumnConstraints(column, dependencyDefinition) {
const table = this.wholeOldDb.tables.find(x => x.pureName == column.pureName && x.schemaName == column.schemaName);
if (!table)
return [];
const fks = (dependencyDefinition === null || dependencyDefinition === void 0 ? void 0 : dependencyDefinition.includes('dependencies'))
? table.dependencies.filter(fk => fk.columns.find(col => col.refColumnName == column.columnName))
: [];
const constraints = lodash_1.default.compact([
(dependencyDefinition === null || dependencyDefinition === void 0 ? void 0 : dependencyDefinition.includes('primaryKey')) ? table.primaryKey : null,
(dependencyDefinition === null || dependencyDefinition === void 0 ? void 0 : dependencyDefinition.includes('sortingKey')) ? table.sortingKey : null,
...((dependencyDefinition === null || dependencyDefinition === void 0 ? void 0 : dependencyDefinition.includes('foreignKeys')) ? table.foreignKeys : []),
...((dependencyDefinition === null || dependencyDefinition === void 0 ? void 0 : dependencyDefinition.includes('indexes')) ? table.indexes : []),
...((dependencyDefinition === null || dependencyDefinition === void 0 ? void 0 : dependencyDefinition.includes('uniques')) ? table.uniques : []),
]).filter(cnt => cnt.columns.find(col => col.columnName == column.columnName));
return [...fks, ...constraints];
}
_addLogicalDependencies() {
const lists = this.operations.map(op => {
if (op.operationType == 'dropColumn') {
const constraints = this._getDependendColumnConstraints(op.oldObject, this.dialect.dropColumnDependencies);
if (constraints.length > 0 && this.opts.noDropConstraint) {
return [];
}
const res = [
...constraints.map(oldObject => {
const opRes = {
operationType: 'dropConstraint',
oldObject,
};
return opRes;
}),
op,
];
return res;
}
for (const [testedOperationType, testedDependencies, testedObject] of [
['changeColumn', this.dialect.changeColumnDependencies, op.oldObject],
['renameColumn', this.dialect.renameColumnDependencies, op.object],
]) {
if (op.operationType == testedOperationType) {
const constraints = this._getDependendColumnConstraints(testedObject, testedDependencies);
const ignoreContraints = this.dialect.safeCommentChanges && this._hasOnlyCommentChange(op);
// if (constraints.length > 0 && this.opts.noDropConstraint) {
// return [];
// }
const res = [];
if (!ignoreContraints) {
res.push(...constraints.map(oldObject => {
const opRes = {
operationType: 'dropConstraint',
oldObject,
isRecreate: true,
};
return opRes;
}));
}
res.push(op);
if (!ignoreContraints) {
res.push(...lodash_1.default.reverse([...constraints]).map(newObject => {
const opRes = {
operationType: 'createConstraint',
newObject,
};
return opRes;
}));
}
if (!ignoreContraints && constraints.length > 0) {
this.recreates.constraints += 1;
}
return res;
}
}
if (op.operationType == 'dropTable') {
return [
...(this.dialect.dropReferencesWhenDropTable
? (op.oldObject.dependencies || []).map(oldObject => {
const opRes = {
operationType: 'dropConstraint',
oldObject,
};
return opRes;
})
: []),
op,
];
}
if (op.operationType == 'changeConstraint') {
// if (this.opts.noDropConstraint) {
// // skip constraint recreate
// return [];
// }
this.recreates.constraints += 1;
const opDrop = {
operationType: 'dropConstraint',
oldObject: op.oldObject,
isRecreate: true,
};
const opCreate = {
operationType: 'createConstraint',
newObject: op.newObject,
};
return [opDrop, opCreate];
}
return [op];
});
return lodash_1.default.flatten(lists);
}
_transformToImplementedOps() {
const lists = this.operations.map(op => {
return (this._testTableRecreate(op, 'createColumn', this.dialect.createColumn, 'newObject') ||
this._testTableRecreate(op, 'dropColumn', this.dialect.dropColumn, 'oldObject') ||
this._testTableRecreate(op, 'createConstraint', obj => this._canCreateConstraint(obj), 'newObject') ||
this._testTableRecreate(op, 'dropConstraint', obj => this._canDropConstraint(obj), 'oldObject') ||
this._testTableRecreate(op, 'changeColumn', this.dialect.changeColumn, 'newObject') ||
// this._testTableRecreate(
// op,
// 'changeColumn',
// obj => this._canChangeAutoIncrement(obj, op as AlterOperation_ChangeColumn),
// 'newObject'
// ) ||
this._testTableRecreate(op, 'renameColumn', true, 'object') || [op]);
});
return lodash_1.default.flatten(lists);
}
_canCreateConstraint(cnt) {
if (cnt.constraintType == 'primaryKey')
return this.dialect.createPrimaryKey;
if (cnt.constraintType == 'sortingKey')
return this.dialect.createPrimaryKey;
if (cnt.constraintType == 'foreignKey')
return this.dialect.createForeignKey;
if (cnt.constraintType == 'index')
return this.dialect.createIndex;
if (cnt.constraintType == 'unique')
return this.dialect.createUnique;
if (cnt.constraintType == 'check')
return this.dialect.createCheck;
return null;
}
_canDropConstraint(cnt) {
if (cnt.constraintType == 'primaryKey')
return this.dialect.dropPrimaryKey;
if (cnt.constraintType == 'sortingKey')
return this.dialect.dropPrimaryKey;
if (cnt.constraintType == 'foreignKey')
return this.dialect.dropForeignKey;
if (cnt.constraintType == 'index')
return this.dialect.dropIndex;
if (cnt.constraintType == 'unique')
return this.dialect.dropUnique;
if (cnt.constraintType == 'check')
return this.dialect.dropCheck;
return null;
}
// _canChangeAutoIncrement(column: ColumnInfo, op: AlterOperation_ChangeColumn) {
// if (!!column.autoIncrement != !!op.oldObject.autoIncrement) {
// return this.dialect.changeAutoIncrement;
// }
// return null;
// }
_testTableRecreate(op, operationType, isAllowed, objectField) {
if (op.operationType == operationType) {
if (lodash_1.default.isFunction(isAllowed)) {
if (isAllowed(op[objectField]))
return null;
}
else {
if (isAllowed)
return null;
}
// console.log('*****************RECREATED NEEDED', op, operationType, isAllowed);
// console.log(this.dialect);
if (this.opts.noDropTable && !this.opts.allowTableRecreate) {
// skip this operation, as it cannot be achieved
return [];
}
const table = this.wholeNewDb.tables.find(x => x.pureName == op[objectField].pureName && x.schemaName == op[objectField].schemaName);
this.recreates.tables += 1;
return [
{
operationType: 'recreateTable',
table,
operations: [op],
},
];
}
return null;
}
_groupTableRecreations() {
const res = [];
const recreates = {};
for (const op of this.operations) {
if (op.operationType == 'recreateTable' && op.table) {
const existingRecreate = recreates[`${op.table.schemaName}||${op.table.pureName}`];
if (existingRecreate) {
existingRecreate.operations.push(...op.operations);
}
else {
const recreate = {
...op,
operations: [...op.operations],
};
res.push(recreate);
recreates[`${op.table.schemaName}||${op.table.pureName}`] = recreate;
}
}
else {
// @ts-ignore
const oldObject = op.oldObject || op.object;
if (oldObject) {
const recreated = recreates[`${oldObject.schemaName}||${oldObject.pureName}`];
if (recreated) {
recreated.operations.push(op);
continue;
}
}
res.push(op);
}
}
return res;
}
_moveForeignKeysToLast() {
if (!this.dialect.createForeignKey) {
return this.operations;
}
const fks = [];
const res = this.operations
.filter(op => op.operationType != 'createConstraint')
.map(op => {
if (op.operationType == 'createTable') {
fks.push(...(op.newObject.foreignKeys || []));
return {
...op,
newObject: {
...op.newObject,
foreignKeys: [],
},
};
}
return op;
});
return [
...res,
...this.operations.filter(op => op.operationType == 'createConstraint'),
...fks.map(fk => ({
operationType: 'createConstraint',
newObject: fk,
})),
];
}
_filterAllowedOperations() {
return this.operations.filter(op => {
if (this.opts.noDropColumn && op.operationType == 'dropColumn')
return false;
if (this.opts.noDropTable && op.operationType == 'dropTable')
return false;
if (this.opts.noDropTable && op.operationType == 'recreateTable')
return false;
if (this.opts.noDropConstraint && op.operationType == 'dropConstraint' && !op.isRecreate)
return false;
// if (
// this.opts.noDropSqlObject &&
// op.operationType == 'dropSqlObject' &&
// // allow to drop previously deleted SQL objects
// !hasDeletedPrefix(op.oldObject.pureName, this.opts, this.opts.deletedSqlObjectPrefix)
// )
// return false;
return true;
});
}
transformPlan() {
// console.log('*****************OPERATIONS0', this.operations);
this.operations = this._addLogicalDependencies();
// console.log('*****************OPERATIONS1', this.operations);
this.operations = this._transformToImplementedOps();
// console.log('*****************OPERATIONS2', this.operations);
this.operations = this._groupTableRecreations();
// console.log('*****************OPERATIONS3', this.operations);
this.operations = this._moveForeignKeysToLast();
// console.log('*****************OPERATIONS4', this.operations);
this.operations = this._filterAllowedOperations();
// console.log('*****************OPERATIONS5', this.operations);
}
}
exports.AlterPlan = AlterPlan;
function runAlterOperation(op, processor) {
switch (op.operationType) {
case 'createTable':
processor.createTable(op.newObject);
break;
case 'changeColumn':
processor.changeColumn(op.oldObject, op.newObject);
break;
case 'createColumn':
processor.createColumn(op.newObject, []);
break;
case 'dropColumn':
processor.dropColumn(op.oldObject);
break;
case 'dropTable':
processor.dropTable(op.oldObject);
break;
case 'changeConstraint':
processor.changeConstraint(op.oldObject, op.newObject);
break;
case 'createConstraint':
processor.createConstraint(op.newObject);
break;
case 'dropConstraint':
processor.dropConstraint(op.oldObject);
break;
case 'renameColumn':
processor.renameColumn(op.object, op.newName);
break;
case 'renameTable':
processor.renameTable(op.object, op.newName);
break;
case 'renameSqlObject':
processor.renameSqlObject(op.object, op.newName);
break;
case 'renameConstraint':
processor.renameConstraint(op.object, op.newName);
break;
case 'createSqlObject':
processor.createSqlObject(op.newObject);
break;
case 'dropSqlObject':
processor.dropSqlObject(op.oldObject);
break;
case 'setTableOption':
processor.setTableOption(op.table, op.optionName, op.optionValue);
break;
case 'fillPreloadedRows':
processor.fillPreloadedRows(op.table, op.oldRows, op.newRows, op.key, op.insertOnly, op.autoIncrementColumn);
break;
case 'recreateTable':
{
const oldTable = (0, diffTools_1.generateTablePairingId)(op.table);
const newTable = lodash_1.default.cloneDeep(oldTable);
const newDb = DatabaseAnalyser_1.DatabaseAnalyser.createEmptyStructure();
newDb.tables.push(newTable);
// console.log('////////////////////////////newTable1', newTable);
op.operations.forEach(child => runAlterOperation(child, new database_info_alter_processor_1.DatabaseInfoAlterProcessor(newDb)));
// console.log('////////////////////////////op.operations', op.operations);
// console.log('////////////////////////////op.table', op.table);
// console.log('////////////////////////////newTable2', newTable);
processor.recreateTable(oldTable, newTable);
}
break;
}
}
exports.runAlterOperation = runAlterOperation;