mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-26 13:16:00 +00:00
alter table WIP
This commit is contained in:
@@ -24,16 +24,20 @@ async function testTableDiff(conn, driver, mangle) {
|
||||
await driver.query(
|
||||
conn,
|
||||
`create table t1 (
|
||||
id int not null primary key,
|
||||
col_pk int not null primary key,
|
||||
col_std int null,
|
||||
col_def int null default 12,
|
||||
col_fk int null references t0(id),
|
||||
col_idx int null
|
||||
col_idx int null,
|
||||
col_uq int null unique,
|
||||
col_ref int null unique
|
||||
)`
|
||||
);
|
||||
|
||||
await driver.query(conn, `create index idx1 on t1(col_idx)`);
|
||||
|
||||
await driver.query(conn, `create table t2 (id int not null primary key, fkval int null references t1(col_ref))`);
|
||||
|
||||
const tget = x => x.tables.find(y => y.pureName == 't1');
|
||||
const structure1 = generateDbPairingId(extendDatabaseInfo(await driver.analyseFull(conn)));
|
||||
let structure2 = _.cloneDeep(structure1);
|
||||
@@ -51,8 +55,10 @@ async function testTableDiff(conn, driver, mangle) {
|
||||
// expect(stableStringify(structure2)).toEqual(stableStringify(structure2Real));
|
||||
}
|
||||
|
||||
// const TESTED_COLUMNS = ['col_std', 'col_def', 'col_fk', 'col_idx'];
|
||||
const TESTED_COLUMNS = ['col_std'];
|
||||
const TESTED_COLUMNS = ['col_pk', 'col_std', 'col_def', 'col_fk', 'col_ref', 'col_idx', 'col_uq'];
|
||||
// const TESTED_COLUMNS = ['col_idx'];
|
||||
// const TESTED_COLUMNS = ['col_fk'];
|
||||
// const TESTED_COLUMNS = ['col_std'];
|
||||
|
||||
function engines_columns_source() {
|
||||
return _.flatten(engines.map(engine => TESTED_COLUMNS.map(column => [engine.label, column, engine])));
|
||||
|
||||
@@ -350,14 +350,33 @@ export class SqlDumper implements AlterProcessor {
|
||||
changeTriggerSchema(obj: TriggerInfo, newSchema: string) {}
|
||||
renameTrigger(obj: TriggerInfo, newSchema: string) {}
|
||||
|
||||
dropConstraint(cnt: ConstraintInfo) {
|
||||
dropConstraintCore(cnt: ConstraintInfo) {
|
||||
this.putCmd('^alter ^table %f ^drop ^constraint %i', cnt, cnt.constraintName);
|
||||
}
|
||||
dropConstraint(cnt: ConstraintInfo) {
|
||||
switch (cnt.constraintType) {
|
||||
case 'primaryKey':
|
||||
this.dropPrimaryKey(cnt as PrimaryKeyInfo);
|
||||
break;
|
||||
case 'foreignKey':
|
||||
this.dropForeignKey(cnt as ForeignKeyInfo);
|
||||
break;
|
||||
case 'unique':
|
||||
this.dropUnique(cnt as UniqueInfo);
|
||||
break;
|
||||
case 'check':
|
||||
this.dropCheck(cnt as CheckInfo);
|
||||
break;
|
||||
case 'index':
|
||||
this.dropIndex(cnt as IndexInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dropForeignKey(fk: ForeignKeyInfo) {
|
||||
if (this.dialect.explicitDropConstraint) {
|
||||
this.putCmd('^alter ^table %f ^drop ^foreign ^key %i', fk, fk.constraintName);
|
||||
} else {
|
||||
this.dropConstraint(fk);
|
||||
this.dropConstraintCore(fk);
|
||||
}
|
||||
}
|
||||
createForeignKey(fk: ForeignKeyInfo) {
|
||||
@@ -369,7 +388,7 @@ export class SqlDumper implements AlterProcessor {
|
||||
if (this.dialect.explicitDropConstraint) {
|
||||
this.putCmd('^alter ^table %f ^drop ^primary ^key', pk);
|
||||
} else {
|
||||
this.dropConstraint(pk);
|
||||
this.dropConstraintCore(pk);
|
||||
}
|
||||
}
|
||||
createPrimaryKey(pk: PrimaryKeyInfo) {
|
||||
@@ -385,7 +404,7 @@ export class SqlDumper implements AlterProcessor {
|
||||
createIndex(ix: IndexInfo) {}
|
||||
|
||||
dropUnique(uq: UniqueInfo) {
|
||||
this.dropConstraint(uq);
|
||||
this.dropConstraintCore(uq);
|
||||
}
|
||||
createUniqueCore(uq: UniqueInfo) {
|
||||
this.put(
|
||||
@@ -402,7 +421,7 @@ export class SqlDumper implements AlterProcessor {
|
||||
}
|
||||
|
||||
dropCheck(ch: CheckInfo) {
|
||||
this.dropConstraint(ch);
|
||||
this.dropConstraintCore(ch);
|
||||
}
|
||||
|
||||
createCheckCore(ch: CheckInfo) {
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { AlterProcessor, ColumnInfo, ConstraintInfo, DatabaseInfo, NamedObjectInfo, TableInfo } from '../../types';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
AlterProcessor,
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
DatabaseInfo,
|
||||
NamedObjectInfo,
|
||||
SqlDialect,
|
||||
TableInfo,
|
||||
} from '../../types';
|
||||
|
||||
interface AlterOperation_CreateTable {
|
||||
operationType: 'createTable';
|
||||
@@ -74,8 +83,8 @@ type AlterOperation =
|
||||
| AlterOperation_RenameConstraint;
|
||||
|
||||
export class AlterPlan {
|
||||
operations: AlterOperation[] = [];
|
||||
constructor(public db: DatabaseInfo) {}
|
||||
public operations: AlterOperation[] = [];
|
||||
constructor(public db: DatabaseInfo, public dialect: SqlDialect) {}
|
||||
|
||||
createTable(table: TableInfo) {
|
||||
this.operations.push({
|
||||
@@ -164,6 +173,45 @@ export class AlterPlan {
|
||||
runAlterOperation(op, processor);
|
||||
}
|
||||
}
|
||||
|
||||
_addLogicalDependencies(): AlterOperation[] {
|
||||
const lists = this.operations.map(op => {
|
||||
if (op.operationType == 'dropColumn') {
|
||||
const table = this.db.tables.find(
|
||||
x => x.pureName == op.oldObject.pureName && x.schemaName == op.oldObject.schemaName
|
||||
);
|
||||
const deletedFks = this.dialect.dropColumnDependencies?.includes('foreignKey')
|
||||
? table.dependencies.filter(fk => fk.columns.find(col => col.refColumnName == op.oldObject.columnName))
|
||||
: [];
|
||||
|
||||
const deletedConstraints = _.compact([
|
||||
table.primaryKey,
|
||||
...table.foreignKeys,
|
||||
...table.indexes,
|
||||
...table.uniques,
|
||||
]).filter(cnt => cnt.columns.find(col => col.columnName == op.oldObject.columnName));
|
||||
|
||||
const res: AlterOperation[] = [
|
||||
...[...deletedFks, ...deletedConstraints].map(oldObject => {
|
||||
const opRes: AlterOperation = {
|
||||
operationType: 'dropConstraint',
|
||||
oldObject,
|
||||
};
|
||||
return opRes;
|
||||
}),
|
||||
op,
|
||||
];
|
||||
return res;
|
||||
}
|
||||
return [op];
|
||||
});
|
||||
|
||||
return _.flatten(lists);
|
||||
}
|
||||
|
||||
transformPlan() {
|
||||
this.operations = this._addLogicalDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
export function runAlterOperation(op: AlterOperation, processor: AlterProcessor) {
|
||||
|
||||
@@ -272,14 +272,16 @@ export function createAlterTablePlan(
|
||||
oldTable: TableInfo,
|
||||
newTable: TableInfo,
|
||||
opts: DbDiffOptions,
|
||||
db: DatabaseInfo
|
||||
db: DatabaseInfo,
|
||||
driver: EngineDriver
|
||||
): AlterPlan {
|
||||
const plan = new AlterPlan(db);
|
||||
const plan = new AlterPlan(db, driver.dialect);
|
||||
if (oldTable == null) {
|
||||
plan.createTable(newTable);
|
||||
} else {
|
||||
planAlterTable(plan, oldTable, newTable, opts);
|
||||
}
|
||||
plan.transformPlan();
|
||||
return plan;
|
||||
}
|
||||
|
||||
@@ -290,7 +292,7 @@ export function getAlterTableScript(
|
||||
db: DatabaseInfo,
|
||||
driver: EngineDriver
|
||||
): string {
|
||||
const plan = createAlterTablePlan(oldTable, newTable, opts, db);
|
||||
const plan = createAlterTablePlan(oldTable, newTable, opts, db, driver);
|
||||
const dmp = driver.createDumper();
|
||||
plan.run(dmp);
|
||||
return dmp.s;
|
||||
|
||||
2
packages/types/alter-processor.d.ts
vendored
2
packages/types/alter-processor.d.ts
vendored
@@ -4,7 +4,7 @@ export interface AlterProcessor {
|
||||
createTable(table: TableInfo);
|
||||
dropTable(table: TableInfo);
|
||||
createColumn(column: ColumnInfo, constraints: ConstraintInfo[]);
|
||||
changeColumn(oldColumn: ColumnInfo, newColumn: ColumnInfo, constraints: ConstraintInfo[]);
|
||||
changeColumn(oldColumn: ColumnInfo, newColumn: ColumnInfo, constraints?: ConstraintInfo[]);
|
||||
dropColumn(column: ColumnInfo);
|
||||
createConstraint(constraint: ConstraintInfo);
|
||||
changeConstraint(oldConstraint: ConstraintInfo, newConstraint: ConstraintInfo);
|
||||
|
||||
2
packages/types/dialect.d.ts
vendored
2
packages/types/dialect.d.ts
vendored
@@ -10,5 +10,7 @@ export interface SqlDialect {
|
||||
explicitDropConstraint?: boolean;
|
||||
anonymousPrimaryKey?: boolean;
|
||||
enableConstraintsPerTable?: boolean;
|
||||
dropColumnDependencies?: string[];
|
||||
changeColumnDependencies?: string[];
|
||||
nosql?: boolean; // mongo
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ const dialect = {
|
||||
fallbackDataType: 'nvarchar(max)',
|
||||
explicitDropConstraint: false,
|
||||
enableConstraintsPerTable: true,
|
||||
dropColumnDependencies: ['default', 'foreignKey', 'index'],
|
||||
changeColumnDependencies: ['index'],
|
||||
anonymousPrimaryKey: false,
|
||||
quoteIdentifier(s) {
|
||||
return `[${s}]`;
|
||||
|
||||
@@ -74,7 +74,7 @@ const drivers = driverBases.map(driverBase => ({
|
||||
}
|
||||
const res = await client.query({ text: sql, rowMode: 'array' });
|
||||
const columns = extractPostgresColumns(res);
|
||||
return { rows: res.rows.map(row => zipDataRow(row, columns)), columns };
|
||||
return { rows: (res.rows || []).map(row => zipDataRow(row, columns)), columns };
|
||||
},
|
||||
stream(client, sql, options) {
|
||||
const query = new pg.Query({
|
||||
|
||||
@@ -14,8 +14,10 @@ const dialect = {
|
||||
limitSelect: true,
|
||||
rangeSelect: true,
|
||||
offsetFetchRangeSyntax: false,
|
||||
explicitDropConstraint: true,
|
||||
stringEscapeChar: "'",
|
||||
fallbackDataType: 'nvarchar(max)',
|
||||
dropColumnDependencies: ['index'],
|
||||
quoteIdentifier(s) {
|
||||
return `[${s}]`;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user