UNPKG

dbgate-tools

Version:

Auxiliary tools for other DbGate packages.

504 lines (503 loc) 20.1 kB
"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;