diff --git a/packages/tools/src/alterPlan.ts b/packages/tools/src/alterPlan.ts new file mode 100644 index 00000000..350d635b --- /dev/null +++ b/packages/tools/src/alterPlan.ts @@ -0,0 +1,117 @@ +import { ColumnInfo, ConstraintInfo, DatabaseInfo, TableInfo } from '../../types'; + +interface AlterOperation_CreateTable { + operationType: 'createTable'; + newObject: TableInfo; +} + +interface AlterOperation_DropTable { + operationType: 'dropTable'; + oldObject: TableInfo; +} + +interface AlterOperation_CreateColumn { + operationType: 'createColumn'; + newObject: ColumnInfo; +} + +interface AlterOperation_ChangeColumn { + operationType: 'changeColumn'; + oldObject: ColumnInfo; + newObject: ColumnInfo; +} + +interface AlterOperation_DropColumn { + operationType: 'dropColumn'; + oldObject: ColumnInfo; +} + +interface AlterOperation_CreateConstraint { + operationType: 'createConstraint'; + newObject: ConstraintInfo; +} + +interface AlterOperation_ChangeConstraint { + operationType: 'changeConstraint'; + oldObject: ConstraintInfo; + newObject: ConstraintInfo; +} + +interface AlterOperation_DropConstraint { + operationType: 'dropConstraint'; + oldObject: ConstraintInfo; +} + +type AlterOperation = + | AlterOperation_CreateColumn + | AlterOperation_ChangeColumn + | AlterOperation_DropColumn + | AlterOperation_CreateConstraint + | AlterOperation_ChangeConstraint + | AlterOperation_DropConstraint + | AlterOperation_CreateTable + | AlterOperation_DropTable; + +export class AlterPlan { + operations: AlterOperation[] = []; + constructor(public db: DatabaseInfo) {} + + createTable(table: TableInfo) { + this.operations.push({ + operationType: 'createTable', + newObject: table, + }); + } + + dropTable(table: TableInfo) { + this.operations.push({ + operationType: 'dropTable', + oldObject: table, + }); + } + + createColumn(column: ColumnInfo) { + this.operations.push({ + operationType: 'createColumn', + newObject: column, + }); + } + + changeColumn(oldColumn: ColumnInfo, newColumn: ColumnInfo) { + this.operations.push({ + operationType: 'changeColumn', + oldObject: oldColumn, + newObject: newColumn, + }); + } + + dropColumn(column: ColumnInfo) { + this.operations.push({ + operationType: 'dropColumn', + oldObject: column, + }); + } + + createConstraint(constraint: ConstraintInfo) { + this.operations.push({ + operationType: 'createConstraint', + newObject: constraint, + }); + } + + changeConstraint(oldConstraint: ConstraintInfo, newConstraint: ConstraintInfo) { + this.operations.push({ + operationType: 'changeConstraint', + oldObject: oldConstraint, + newObject: newConstraint, + }); + } + + dropConstraint(constraint: ConstraintInfo) { + this.operations.push({ + operationType: 'dropConstraint', + oldObject: constraint, + }); + } + +} diff --git a/packages/tools/src/diffTools.ts b/packages/tools/src/diffTools.ts index fe5a6075..cd0849a1 100644 --- a/packages/tools/src/diffTools.ts +++ b/packages/tools/src/diffTools.ts @@ -1,5 +1,13 @@ -import { ColumnInfo, TableInfo } from 'dbgate-types'; +import { ColumnInfo, DatabaseInfo, TableInfo } from 'dbgate-types'; import uuidv1 from 'uuid/v1'; +import { AlterPlan } from './alterPlan'; + +export interface DbDiffOptions { + allowRecreateTable: boolean; + allowRecreateConstraint: boolean; + allowRecreateSpecificObject: boolean; + allowPairRenamedTables: boolean; +} export function generateTablePairingId(table: TableInfo): TableInfo { if (!table) return table; @@ -27,3 +35,58 @@ export function generateTablePairingId(table: TableInfo): TableInfo { } return table; } + +function getTableConstraints(table: TableInfo) { + const res = []; + if (table.primaryKey) res.push(table.primaryKey); + if (table.foreignKeys) res.push(...table.foreignKeys); + if (table.indexes) res.push(...table.indexes); + if (table.checks) res.push(...table.checks); + return res; +} + +function createPairs(oldList, newList, additionalCondition = null) { + const res = []; + for (const a of oldList) { + const b = newList.find(x => x.pairingId == a.pairingId || (additionalCondition && additionalCondition(a, b))); + if (b) { + res.push([a, b]); + } else { + res.push([a, null]); + } + } + for (const b of newList) { + if (!res.find(x => x[1] == b)) { + res.push([null, b]); + } + } + return res; +} + +function planAlterTable(plan: AlterPlan, oldTable: TableInfo, newTable: TableInfo, options: DbDiffOptions) { + // if (oldTable.primaryKey) + + const constraintPairs = createPairs( + getTableConstraints(oldTable), + getTableConstraints(newTable), + (a, b) => a.constraintType == 'primaryKey' && b.constraintType == 'primaryKey' + ); + const columnPairs = createPairs(oldTable.columns, newTable.columns); + + constraintPairs.filter(x => x[1] == null).forEach(x => plan.dropConstraint(x)); +} + +export function createAlterTablePlan( + oldTable: TableInfo, + newTable: TableInfo, + options: DbDiffOptions, + db: DatabaseInfo +): AlterPlan { + const plan = new AlterPlan(db); + if (oldTable == null) { + plan.createTable(newTable); + } else { + planAlterTable(plan, oldTable, newTable, options); + } + return plan; +}