dbgate-tools 4.3.4 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import { DbDiffOptions } from './diffTools';
1
2
  import { AlterProcessor, ColumnInfo, ConstraintInfo, DatabaseInfo, SqlObjectInfo, SqlDialect, TableInfo } from '../../types';
2
3
  interface AlterOperation_CreateTable {
3
4
  operationType: 'createTable';
@@ -65,13 +66,14 @@ declare type AlterOperation = AlterOperation_CreateColumn | AlterOperation_Chang
65
66
  export declare class AlterPlan {
66
67
  db: DatabaseInfo;
67
68
  dialect: SqlDialect;
69
+ opts: DbDiffOptions;
68
70
  recreates: {
69
71
  tables: number;
70
72
  constraints: number;
71
73
  sqlObjects: number;
72
74
  };
73
75
  operations: AlterOperation[];
74
- constructor(db: DatabaseInfo, dialect: SqlDialect);
76
+ constructor(db: DatabaseInfo, dialect: SqlDialect, opts: DbDiffOptions);
75
77
  createTable(table: TableInfo): void;
76
78
  dropTable(table: TableInfo): void;
77
79
  createSqlObject(obj: SqlObjectInfo): void;
@@ -94,6 +96,8 @@ export declare class AlterPlan {
94
96
  _canDropConstraint(cnt: ConstraintInfo): boolean;
95
97
  _testTableRecreate(op: AlterOperation, operationType: string, isAllowed: boolean | Function, objectField: string): AlterOperation[] | null;
96
98
  _groupTableRecreations(): AlterOperation[];
99
+ _moveForeignKeysToLast(): AlterOperation[];
100
+ _filterAllowedOperations(): AlterOperation[];
97
101
  transformPlan(): void;
98
102
  }
99
103
  export declare function runAlterOperation(op: AlterOperation, processor: AlterProcessor): void;
package/lib/alterPlan.js CHANGED
@@ -5,13 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runAlterOperation = exports.AlterPlan = void 0;
7
7
  const lodash_1 = __importDefault(require("lodash"));
8
- const _1 = require(".");
8
+ const diffTools_1 = require("./diffTools");
9
9
  const database_info_alter_processor_1 = require("./database-info-alter-processor");
10
10
  const DatabaseAnalyser_1 = require("./DatabaseAnalyser");
11
11
  class AlterPlan {
12
- constructor(db, dialect) {
12
+ constructor(db, dialect, opts) {
13
13
  this.db = db;
14
14
  this.dialect = dialect;
15
+ this.opts = opts;
15
16
  this.recreates = {
16
17
  tables: 0,
17
18
  constraints: 0,
@@ -117,6 +118,8 @@ class AlterPlan {
117
118
  }
118
119
  _getDependendColumnConstraints(column, dependencyDefinition) {
119
120
  const table = this.db.tables.find(x => x.pureName == column.pureName && x.schemaName == column.schemaName);
121
+ if (!table)
122
+ return [];
120
123
  const fks = (dependencyDefinition === null || dependencyDefinition === void 0 ? void 0 : dependencyDefinition.includes('dependencies'))
121
124
  ? table.dependencies.filter(fk => fk.columns.find(col => col.refColumnName == column.columnName))
122
125
  : [];
@@ -132,6 +135,9 @@ class AlterPlan {
132
135
  const lists = this.operations.map(op => {
133
136
  if (op.operationType == 'dropColumn') {
134
137
  const constraints = this._getDependendColumnConstraints(op.oldObject, this.dialect.dropColumnDependencies);
138
+ if (constraints.length > 0 && this.opts.noDropConstraint) {
139
+ return [];
140
+ }
135
141
  const res = [
136
142
  ...constraints.map(oldObject => {
137
143
  const opRes = {
@@ -146,6 +152,9 @@ class AlterPlan {
146
152
  }
147
153
  if (op.operationType == 'changeColumn') {
148
154
  const constraints = this._getDependendColumnConstraints(op.oldObject, this.dialect.changeColumnDependencies);
155
+ if (constraints.length > 0 && this.opts.noDropConstraint) {
156
+ return [];
157
+ }
149
158
  const res = [
150
159
  ...constraints.map(oldObject => {
151
160
  const opRes = {
@@ -183,6 +192,10 @@ class AlterPlan {
183
192
  ];
184
193
  }
185
194
  if (op.operationType == 'changeConstraint') {
195
+ if (this.opts.noDropConstraint) {
196
+ // skip constraint recreate
197
+ return [];
198
+ }
186
199
  this.recreates.constraints += 1;
187
200
  const opDrop = {
188
201
  operationType: 'dropConstraint',
@@ -246,6 +259,10 @@ class AlterPlan {
246
259
  }
247
260
  // console.log('*****************RECREATED NEEDED', op, operationType, isAllowed);
248
261
  // console.log(this.dialect);
262
+ if (this.opts.noDropTable) {
263
+ // skip this operation, as it cannot be achieved
264
+ return [];
265
+ }
249
266
  const table = this.db.tables.find(x => x.pureName == op[objectField].pureName && x.schemaName == op[objectField].schemaName);
250
267
  this.recreates.tables += 1;
251
268
  return [
@@ -262,7 +279,7 @@ class AlterPlan {
262
279
  const res = [];
263
280
  const recreates = {};
264
281
  for (const op of this.operations) {
265
- if (op.operationType == 'recreateTable') {
282
+ if (op.operationType == 'recreateTable' && op.table) {
266
283
  const existingRecreate = recreates[`${op.table.schemaName}||${op.table.pureName}`];
267
284
  if (existingRecreate) {
268
285
  existingRecreate.operations.push(...op.operations);
@@ -288,6 +305,41 @@ class AlterPlan {
288
305
  }
289
306
  return res;
290
307
  }
308
+ _moveForeignKeysToLast() {
309
+ if (!this.dialect.createForeignKey) {
310
+ return this.operations;
311
+ }
312
+ const fks = [];
313
+ const res = this.operations.map(op => {
314
+ if (op.operationType == 'createTable') {
315
+ fks.push(...(op.newObject.foreignKeys || []));
316
+ return Object.assign(Object.assign({}, op), { newObject: Object.assign(Object.assign({}, op.newObject), { foreignKeys: [] }) });
317
+ }
318
+ return op;
319
+ });
320
+ return [
321
+ ...res,
322
+ ...fks.map(fk => ({
323
+ operationType: 'createConstraint',
324
+ newObject: fk,
325
+ })),
326
+ ];
327
+ }
328
+ _filterAllowedOperations() {
329
+ return this.operations.filter(op => {
330
+ if (this.opts.noDropColumn && op.operationType == 'dropColumn')
331
+ return false;
332
+ if (this.opts.noDropTable && op.operationType == 'dropTable')
333
+ return false;
334
+ if (this.opts.noDropTable && op.operationType == 'recreateTable')
335
+ return false;
336
+ if (this.opts.noDropConstraint && op.operationType == 'dropConstraint')
337
+ return false;
338
+ if (this.opts.noDropSqlObject && op.operationType == 'dropSqlObject')
339
+ return false;
340
+ return true;
341
+ });
342
+ }
291
343
  transformPlan() {
292
344
  // console.log('*****************OPERATIONS0', this.operations);
293
345
  this.operations = this._addLogicalDependencies();
@@ -296,6 +348,10 @@ class AlterPlan {
296
348
  // console.log('*****************OPERATIONS2', this.operations);
297
349
  this.operations = this._groupTableRecreations();
298
350
  // console.log('*****************OPERATIONS3', this.operations);
351
+ this.operations = this._moveForeignKeysToLast();
352
+ // console.log('*****************OPERATIONS4', this.operations);
353
+ this.operations = this._filterAllowedOperations();
354
+ // console.log('*****************OPERATIONS5', this.operations);
299
355
  }
300
356
  }
301
357
  exports.AlterPlan = AlterPlan;
@@ -342,7 +398,7 @@ function runAlterOperation(op, processor) {
342
398
  break;
343
399
  case 'recreateTable':
344
400
  {
345
- const oldTable = (0, _1.generateTablePairingId)(op.table);
401
+ const oldTable = (0, diffTools_1.generateTablePairingId)(op.table);
346
402
  const newTable = lodash_1.default.cloneDeep(oldTable);
347
403
  const newDb = DatabaseAnalyser_1.DatabaseAnalyser.createEmptyStructure();
348
404
  newDb.tables.push(newTable);
@@ -0,0 +1,38 @@
1
+ import { DbDiffOptions, testEqualTables, testEqualSqlObjects } from './diffTools';
2
+ import { DatabaseInfo, EngineDriver, SqlObjectInfo, TableInfo } from 'dbgate-types';
3
+ export declare function computeDiffRowsCore(sourceList: any, targetList: any, testEqual: any): any[];
4
+ export declare const DbDiffCompareDefs: {
5
+ tables: {
6
+ test: typeof testEqualTables;
7
+ name: string;
8
+ plural: string;
9
+ icon: string;
10
+ };
11
+ views: {
12
+ test: typeof testEqualSqlObjects;
13
+ name: string;
14
+ plural: string;
15
+ icon: string;
16
+ };
17
+ matviews: {
18
+ test: typeof testEqualSqlObjects;
19
+ name: string;
20
+ plural: string;
21
+ icon: string;
22
+ };
23
+ procedures: {
24
+ test: typeof testEqualSqlObjects;
25
+ name: string;
26
+ plural: string;
27
+ icon: string;
28
+ };
29
+ functions: {
30
+ test: typeof testEqualSqlObjects;
31
+ name: string;
32
+ plural: string;
33
+ icon: string;
34
+ };
35
+ };
36
+ export declare function computeDbDiffRows(sourceDb: DatabaseInfo, targetDb: DatabaseInfo, opts: DbDiffOptions, driver: EngineDriver): any[];
37
+ export declare function computeTableDiffColumns(sourceTable: TableInfo, targetTable: TableInfo, opts: DbDiffOptions, driver: EngineDriver): any[];
38
+ export declare function getCreateObjectScript(obj: TableInfo | SqlObjectInfo, driver: EngineDriver): string;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getCreateObjectScript = exports.computeTableDiffColumns = exports.computeDbDiffRows = exports.DbDiffCompareDefs = exports.computeDiffRowsCore = void 0;
7
+ const diffTools_1 = require("./diffTools");
8
+ const lodash_1 = __importDefault(require("lodash"));
9
+ function computeDiffRowsCore(sourceList, targetList, testEqual) {
10
+ const res = [];
11
+ for (const obj of sourceList) {
12
+ const paired = targetList.find(x => x.pairingId == obj.pairingId);
13
+ if (paired) {
14
+ const isEqual = testEqual(obj, paired);
15
+ res.push({
16
+ source: obj,
17
+ target: paired,
18
+ state: isEqual ? 'equal' : 'changed',
19
+ __isChanged: !isEqual,
20
+ });
21
+ }
22
+ else {
23
+ res.push({
24
+ source: obj,
25
+ state: 'added',
26
+ __isAdded: true,
27
+ });
28
+ }
29
+ }
30
+ for (const obj of targetList) {
31
+ const paired = sourceList.find(x => x.pairingId == obj.pairingId);
32
+ if (!paired) {
33
+ res.push({
34
+ target: obj,
35
+ state: 'removed',
36
+ __isDeleted: true,
37
+ });
38
+ }
39
+ }
40
+ return res;
41
+ }
42
+ exports.computeDiffRowsCore = computeDiffRowsCore;
43
+ exports.DbDiffCompareDefs = {
44
+ tables: {
45
+ test: diffTools_1.testEqualTables,
46
+ name: 'Table',
47
+ plural: 'Tables',
48
+ icon: 'img table',
49
+ },
50
+ views: {
51
+ test: diffTools_1.testEqualSqlObjects,
52
+ name: 'View',
53
+ plural: 'Views',
54
+ icon: 'img view',
55
+ },
56
+ matviews: {
57
+ test: diffTools_1.testEqualSqlObjects,
58
+ name: 'Materialized view',
59
+ plural: 'Materialized views',
60
+ icon: 'img view',
61
+ },
62
+ procedures: {
63
+ test: diffTools_1.testEqualSqlObjects,
64
+ name: 'Procedure',
65
+ plural: 'Procedures',
66
+ icon: 'img procedure',
67
+ },
68
+ functions: {
69
+ test: diffTools_1.testEqualSqlObjects,
70
+ name: 'Function',
71
+ plural: 'Functions',
72
+ icon: 'img function',
73
+ },
74
+ };
75
+ function computeDbDiffRows(sourceDb, targetDb, opts, driver) {
76
+ if (!sourceDb || !targetDb || !driver)
77
+ return [];
78
+ const res = [];
79
+ for (const objectTypeField of ['tables', 'views', 'procedures', 'matviews', 'functions']) {
80
+ const defs = exports.DbDiffCompareDefs[objectTypeField];
81
+ res.push(...lodash_1.default.sortBy(computeDiffRowsCore(sourceDb[objectTypeField], targetDb[objectTypeField], (a, b) => defs.test(a, b, opts, targetDb, driver)).map(row => {
82
+ var _a, _b, _c, _d, _e, _f, _g, _h;
83
+ return (Object.assign(Object.assign({}, row), { sourceSchemaName: (_a = row === null || row === void 0 ? void 0 : row.source) === null || _a === void 0 ? void 0 : _a.schemaName, sourcePureName: (_b = row === null || row === void 0 ? void 0 : row.source) === null || _b === void 0 ? void 0 : _b.pureName, targetSchemaName: (_c = row === null || row === void 0 ? void 0 : row.target) === null || _c === void 0 ? void 0 : _c.schemaName, targetPureName: (_d = row === null || row === void 0 ? void 0 : row.target) === null || _d === void 0 ? void 0 : _d.pureName, typeName: defs.name, typeIcon: defs.icon, identifier: `${((_e = row === null || row === void 0 ? void 0 : row.source) === null || _e === void 0 ? void 0 : _e.schemaName) || ((_f = row === null || row === void 0 ? void 0 : row.target) === null || _f === void 0 ? void 0 : _f.schemaName)}.${((_g = row === null || row === void 0 ? void 0 : row.source) === null || _g === void 0 ? void 0 : _g.pureName) || ((_h = row === null || row === void 0 ? void 0 : row.target) === null || _h === void 0 ? void 0 : _h.pureName)}`, objectTypeField }));
84
+ }), 'identifier'));
85
+ }
86
+ return res;
87
+ }
88
+ exports.computeDbDiffRows = computeDbDiffRows;
89
+ function computeTableDiffColumns(sourceTable, targetTable, opts, driver) {
90
+ if (!driver)
91
+ return [];
92
+ return computeDiffRowsCore((sourceTable === null || sourceTable === void 0 ? void 0 : sourceTable.columns) || [], (targetTable === null || targetTable === void 0 ? void 0 : targetTable.columns) || [], (a, b) => (0, diffTools_1.testEqualColumns)(a, b, true, true, opts)).map(row => {
93
+ var _a, _b, _c, _d, _e, _f;
94
+ return (Object.assign(Object.assign({}, row), { sourceColumnName: (_a = row === null || row === void 0 ? void 0 : row.source) === null || _a === void 0 ? void 0 : _a.columnName, targetColumnName: (_b = row === null || row === void 0 ? void 0 : row.target) === null || _b === void 0 ? void 0 : _b.columnName, sourceDataType: (_c = row === null || row === void 0 ? void 0 : row.source) === null || _c === void 0 ? void 0 : _c.dataType, targetDataType: (_d = row === null || row === void 0 ? void 0 : row.target) === null || _d === void 0 ? void 0 : _d.dataType, sourceNotNull: (_e = row === null || row === void 0 ? void 0 : row.source) === null || _e === void 0 ? void 0 : _e.notNull, targetNotNull: (_f = row === null || row === void 0 ? void 0 : row.target) === null || _f === void 0 ? void 0 : _f.notNull }));
95
+ });
96
+ }
97
+ exports.computeTableDiffColumns = computeTableDiffColumns;
98
+ function getCreateObjectScript(obj, driver) {
99
+ if (!obj || !driver)
100
+ return '';
101
+ if (obj.objectTypeField == 'tables') {
102
+ const dmp = driver.createDumper();
103
+ dmp.createTable(obj);
104
+ return dmp.s;
105
+ }
106
+ return obj.createSql || '';
107
+ }
108
+ exports.getCreateObjectScript = getCreateObjectScript;
@@ -23,14 +23,20 @@ class DatabaseInfoAlterProcessor {
23
23
  }
24
24
  createColumn(column) {
25
25
  const table = this.db.tables.find(x => x.pureName == column.pureName && x.schemaName == column.schemaName);
26
+ if (!table)
27
+ throw new Error(`createColumn error, cannot find table: ${column.schemaName}.${column.pureName}`);
26
28
  table.columns.push(column);
27
29
  }
28
30
  changeColumn(oldColumn, newColumn) {
29
31
  const table = this.db.tables.find(x => x.pureName == oldColumn.pureName && x.schemaName == oldColumn.schemaName);
32
+ if (!table)
33
+ throw new Error(`changeColumn error, cannot find table: ${oldColumn.schemaName}.${oldColumn.pureName}`);
30
34
  table.columns = table.columns.map(x => (x.columnName == oldColumn.columnName ? newColumn : x));
31
35
  }
32
36
  dropColumn(column) {
33
37
  const table = this.db.tables.find(x => x.pureName == column.pureName && x.schemaName == column.schemaName);
38
+ if (!table)
39
+ throw new Error(`dropColumn error, cannot find table: ${column.schemaName}.${column.pureName}`);
34
40
  lodash_1.default.remove(table.columns, x => x.columnName == column.columnName);
35
41
  }
36
42
  createConstraint(constraint) {
@@ -1,23 +1,33 @@
1
- import { ColumnInfo, DatabaseInfo, EngineDriver, TableInfo } from 'dbgate-types';
1
+ import { ColumnInfo, DatabaseInfo, EngineDriver, SqlObjectInfo, TableInfo } from 'dbgate-types';
2
2
  import { AlterPlan } from './alterPlan';
3
3
  declare type DbDiffSchemaMode = 'strict' | 'ignore' | 'ignoreImplicit';
4
4
  export interface DbDiffOptions {
5
- allowRecreateTable?: boolean;
6
- allowRecreateConstraint?: boolean;
7
- allowRecreateSpecificObject?: boolean;
8
- allowPairRenamedTables?: boolean;
9
5
  ignoreCase?: boolean;
10
6
  schemaMode?: DbDiffSchemaMode;
11
7
  leftImplicitSchema?: string;
12
8
  rightImplicitSchema?: string;
9
+ ignoreConstraintNames?: boolean;
10
+ noDropTable?: boolean;
11
+ noDropColumn?: boolean;
12
+ noDropConstraint?: boolean;
13
+ noDropSqlObject?: boolean;
14
+ noRenameTable?: boolean;
15
+ noRenameColumn?: boolean;
16
+ ignoreForeignKeyActions?: boolean;
17
+ ignoreDataTypes?: boolean;
13
18
  }
14
19
  export declare function generateTablePairingId(table: TableInfo): TableInfo;
15
20
  export declare function generateDbPairingId(db: DatabaseInfo): DatabaseInfo;
16
21
  export declare function testEqualColumns(a: ColumnInfo, b: ColumnInfo, checkName: boolean, checkDefault: boolean, opts?: DbDiffOptions): boolean;
17
22
  export declare function testEqualTypes(a: ColumnInfo, b: ColumnInfo, opts?: DbDiffOptions): boolean;
23
+ export declare function testEqualTables(a: TableInfo, b: TableInfo, opts: DbDiffOptions, db: DatabaseInfo, driver: EngineDriver): boolean;
24
+ export declare function testEqualSqlObjects(a: SqlObjectInfo, b: SqlObjectInfo, opts: DbDiffOptions): boolean;
18
25
  export declare function createAlterTablePlan(oldTable: TableInfo, newTable: TableInfo, opts: DbDiffOptions, db: DatabaseInfo, driver: EngineDriver): AlterPlan;
19
26
  export declare function createAlterDatabasePlan(oldDb: DatabaseInfo, newDb: DatabaseInfo, opts: DbDiffOptions, db: DatabaseInfo, driver: EngineDriver): AlterPlan;
20
27
  export declare function getAlterTableScript(oldTable: TableInfo, newTable: TableInfo, opts: DbDiffOptions, db: DatabaseInfo, driver: EngineDriver): {
28
+ sql: string;
29
+ recreates: any[];
30
+ } | {
21
31
  sql: string;
22
32
  recreates: {
23
33
  tables: number;
@@ -32,5 +42,8 @@ export declare function getAlterDatabaseScript(oldDb: DatabaseInfo, newDb: Datab
32
42
  constraints: number;
33
43
  sqlObjects: number;
34
44
  };
45
+ isEmpty: boolean;
35
46
  };
47
+ export declare function matchPairedObjects(db1: DatabaseInfo, db2: DatabaseInfo, opts: DbDiffOptions): DatabaseInfo;
48
+ export declare const modelCompareDbDiffOptions: DbDiffOptions;
36
49
  export {};
package/lib/diffTools.js CHANGED
@@ -3,10 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getAlterDatabaseScript = exports.getAlterTableScript = exports.createAlterDatabasePlan = exports.createAlterTablePlan = exports.testEqualTypes = exports.testEqualColumns = exports.generateDbPairingId = exports.generateTablePairingId = void 0;
6
+ exports.modelCompareDbDiffOptions = exports.matchPairedObjects = exports.getAlterDatabaseScript = exports.getAlterTableScript = exports.createAlterDatabasePlan = exports.createAlterTablePlan = exports.testEqualSqlObjects = exports.testEqualTables = exports.testEqualTypes = exports.testEqualColumns = exports.generateDbPairingId = exports.generateTablePairingId = void 0;
7
7
  const v1_1 = __importDefault(require("uuid/v1"));
8
8
  const alterPlan_1 = require("./alterPlan");
9
9
  const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
10
+ const omit_1 = __importDefault(require("lodash/omit"));
11
+ const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
12
+ const isEqual_1 = __importDefault(require("lodash/isEqual"));
13
+ const pick_1 = __importDefault(require("lodash/pick"));
10
14
  function generateTablePairingId(table) {
11
15
  var _a, _b, _c, _d, _e;
12
16
  if (!table)
@@ -33,7 +37,7 @@ function generateDbPairingId(db) {
33
37
  exports.generateDbPairingId = generateDbPairingId;
34
38
  function testEqualNames(a, b, opts) {
35
39
  if (opts.ignoreCase)
36
- return a.toLowerCase() == b.toLowerCase();
40
+ return (a || '').toLowerCase() == (b || '').toLowerCase();
37
41
  return a == b;
38
42
  }
39
43
  function testEqualSchemas(lschema, rschema, opts) {
@@ -63,6 +67,7 @@ function testEqualColumns(a, b, checkName, checkDefault, opts = {}) {
63
67
  // return false;
64
68
  //}
65
69
  if (a.computedExpression != b.computedExpression) {
70
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different computed expression: ${a.computedExpression}, ${b.computedExpression}`);
66
71
  // opts.DiffLogger.Trace(
67
72
  // 'Column {0}, {1}: different computed expression: {2}; {3}',
68
73
  // a,
@@ -78,6 +83,7 @@ function testEqualColumns(a, b, checkName, checkDefault, opts = {}) {
78
83
  if (checkDefault) {
79
84
  if (a.defaultValue == null) {
80
85
  if (a.defaultValue != b.defaultValue) {
86
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different default value: ${a.defaultValue}, ${b.defaultValue}`);
81
87
  // opts.DiffLogger.Trace(
82
88
  // 'Column {0}, {1}: different default values: {2}; {3}',
83
89
  // a,
@@ -90,6 +96,7 @@ function testEqualColumns(a, b, checkName, checkDefault, opts = {}) {
90
96
  }
91
97
  else {
92
98
  if (a.defaultValue != b.defaultValue) {
99
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different default value: ${a.defaultValue}, ${b.defaultValue}`);
93
100
  // opts.DiffLogger.Trace(
94
101
  // 'Column {0}, {1}: different default values: {2}; {3}',
95
102
  // a,
@@ -101,6 +108,7 @@ function testEqualColumns(a, b, checkName, checkDefault, opts = {}) {
101
108
  }
102
109
  }
103
110
  if (a.defaultConstraint != b.defaultConstraint) {
111
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different default constraint: ${a.defaultConstraint}, ${b.defaultConstraint}`);
104
112
  // opts.DiffLogger.Trace(
105
113
  // 'Column {0}, {1}: different default constraint names: {2}; {3}',
106
114
  // a,
@@ -111,15 +119,18 @@ function testEqualColumns(a, b, checkName, checkDefault, opts = {}) {
111
119
  return false;
112
120
  }
113
121
  }
114
- if (a.notNull != b.notNull) {
122
+ if ((a.notNull || false) != (b.notNull || false)) {
123
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different nullability: ${a.notNull}, ${b.notNull}`);
115
124
  // opts.DiffLogger.Trace('Column {0}, {1}: different nullable: {2}; {3}', a, b, a.NotNull, b.NotNull);
116
125
  return false;
117
126
  }
118
- if (a.autoIncrement != b.autoIncrement) {
127
+ if ((a.autoIncrement || false) != (b.autoIncrement || false)) {
128
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different autoincrement: ${a.autoIncrement}, ${b.autoIncrement}`);
119
129
  // opts.DiffLogger.Trace('Column {0}, {1}: different autoincrement: {2}; {3}', a, b, a.AutoIncrement, b.AutoIncrement);
120
130
  return false;
121
131
  }
122
- if (a.isSparse != b.isSparse) {
132
+ if ((a.isSparse || false) != (b.isSparse || false)) {
133
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different is_sparse: ${a.isSparse}, ${b.isSparse}`);
123
134
  // opts.DiffLogger.Trace('Column {0}, {1}: different is_sparse: {2}; {3}', a, b, a.IsSparse, b.IsSparse);
124
135
  return false;
125
136
  }
@@ -154,28 +165,36 @@ function testEqualColumns(a, b, checkName, checkDefault, opts = {}) {
154
165
  }
155
166
  exports.testEqualColumns = testEqualColumns;
156
167
  function testEqualConstraints(a, b, opts = {}) {
157
- return (0, json_stable_stringify_1.default)(a) == (0, json_stable_stringify_1.default)(b);
168
+ const omitList = [];
169
+ if (opts.ignoreForeignKeyActions) {
170
+ omitList.push('updateAction');
171
+ omitList.push('deleteAction');
172
+ }
173
+ if (opts.ignoreConstraintNames) {
174
+ omitList.push('constraintName');
175
+ }
176
+ if (opts.schemaMode == 'ignore') {
177
+ omitList.push('schemaName');
178
+ omitList.push('refSchemaName');
179
+ }
180
+ // if (a.constraintType == 'primaryKey' && b.constraintType == 'primaryKey') {
181
+ // console.log('PK1', stableStringify(_.omit(a, omitList)));
182
+ // console.log('PK2', stableStringify(_.omit(b, omitList)));
183
+ // }
184
+ // if (a.constraintType == 'foreignKey' && b.constraintType == 'foreignKey') {
185
+ // console.log('FK1', stableStringify(_omit(a, omitList)));
186
+ // console.log('FK2', stableStringify(_omit(b, omitList)));
187
+ // }
188
+ return (0, json_stable_stringify_1.default)((0, omit_1.default)(a, omitList)) == (0, json_stable_stringify_1.default)((0, omit_1.default)(b, omitList));
158
189
  }
159
190
  function testEqualTypes(a, b, opts = {}) {
160
- if (a.dataType != b.dataType) {
161
- // opts.DiffLogger.Trace("Column {0}, {1}: different types: {2}; {3}", a, b, a.DataType, b.DataType);
191
+ if (opts.ignoreDataTypes) {
192
+ return true;
193
+ }
194
+ if ((a.dataType || '').toLowerCase() != (b.dataType || '').toLowerCase()) {
195
+ console.debug(`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different data type: ${a.dataType}, ${b.dataType}`);
162
196
  return false;
163
197
  }
164
- //if (a.Length != b.Length)
165
- //{
166
- // opts.DiffLogger.Trace("Column {0}, {1}: different lengths: {2}; {3}", a, b, a.Length, b.Length);
167
- // return false;
168
- //}
169
- //if (a.Precision != b.Precision)
170
- //{
171
- // opts.DiffLogger.Trace("Column {0}, {1}: different lengths: {2}; {3}", a, b, a.Precision, b.Precision);
172
- // return false;
173
- //}
174
- //if (a.Scale != b.Scale)
175
- //{
176
- // opts.DiffLogger.Trace("Column {0}, {1}: different scale: {2}; {3}", a, b, a.Scale, b.Scale);
177
- // return false;
178
- //}
179
198
  return true;
180
199
  }
181
200
  exports.testEqualTypes = testEqualTypes;
@@ -215,9 +234,13 @@ function planAlterTable(plan, oldTable, newTable, opts) {
215
234
  // if (oldTable.primaryKey)
216
235
  const columnPairs = createPairs(oldTable.columns, newTable.columns);
217
236
  const constraintPairs = createPairs(getTableConstraints(oldTable), getTableConstraints(newTable), (a, b) => a.constraintType == 'primaryKey' && b.constraintType == 'primaryKey');
218
- constraintPairs.filter(x => x[1] == null).forEach(x => plan.dropConstraint(x[0]));
219
- columnPairs.filter(x => x[1] == null).forEach(x => plan.dropColumn(x[0]));
220
- if (!testEqualFullNames(oldTable, newTable, opts)) {
237
+ if (!opts.noDropConstraint) {
238
+ constraintPairs.filter(x => x[1] == null).forEach(x => plan.dropConstraint(x[0]));
239
+ }
240
+ if (!opts.noDropColumn) {
241
+ columnPairs.filter(x => x[1] == null).forEach(x => plan.dropColumn(x[0]));
242
+ }
243
+ if (!testEqualFullNames(oldTable, newTable, opts) && !opts.noRenameTable) {
221
244
  plan.renameTable(oldTable, newTable.pureName);
222
245
  }
223
246
  columnPairs.filter(x => x[0] == null).forEach(x => plan.createColumn(x[1]));
@@ -225,7 +248,7 @@ function planAlterTable(plan, oldTable, newTable, opts) {
225
248
  .filter(x => x[0] && x[1])
226
249
  .forEach(x => {
227
250
  if (!testEqualColumns(x[0], x[1], true, true, opts)) {
228
- if (testEqualColumns(x[0], x[1], false, true, opts)) {
251
+ if (testEqualColumns(x[0], x[1], false, true, opts) && !opts.noRenameColumn) {
229
252
  // console.log('PLAN RENAME COLUMN')
230
253
  plan.renameColumn(x[0], x[1].columnName);
231
254
  }
@@ -245,11 +268,25 @@ function planAlterTable(plan, oldTable, newTable, opts) {
245
268
  });
246
269
  constraintPairs.filter(x => x[0] == null).forEach(x => plan.createConstraint(x[1]));
247
270
  }
271
+ function testEqualTables(a, b, opts, db, driver) {
272
+ const plan = new alterPlan_1.AlterPlan(db, driver.dialect, opts);
273
+ planAlterTable(plan, a, b, opts);
274
+ // console.log('plan.operations', a, b, plan.operations);
275
+ return plan.operations.length == 0;
276
+ }
277
+ exports.testEqualTables = testEqualTables;
278
+ function testEqualSqlObjects(a, b, opts) {
279
+ return a.createSql == b.createSql;
280
+ }
281
+ exports.testEqualSqlObjects = testEqualSqlObjects;
248
282
  function createAlterTablePlan(oldTable, newTable, opts, db, driver) {
249
- const plan = new alterPlan_1.AlterPlan(db, driver.dialect);
283
+ const plan = new alterPlan_1.AlterPlan(db, driver.dialect, opts);
250
284
  if (oldTable == null) {
251
285
  plan.createTable(newTable);
252
286
  }
287
+ else if (newTable == null) {
288
+ plan.dropTable(oldTable);
289
+ }
253
290
  else {
254
291
  planAlterTable(plan, oldTable, newTable, opts);
255
292
  }
@@ -258,22 +295,31 @@ function createAlterTablePlan(oldTable, newTable, opts, db, driver) {
258
295
  }
259
296
  exports.createAlterTablePlan = createAlterTablePlan;
260
297
  function createAlterDatabasePlan(oldDb, newDb, opts, db, driver) {
261
- const plan = new alterPlan_1.AlterPlan(db, driver.dialect);
298
+ const plan = new alterPlan_1.AlterPlan(db, driver.dialect, opts);
262
299
  for (const objectTypeField of ['tables', 'views', 'procedures', 'matviews', 'functions']) {
263
300
  for (const oldobj of oldDb[objectTypeField] || []) {
264
301
  const newobj = (newDb[objectTypeField] || []).find(x => x.pairingId == oldobj.pairingId);
265
302
  if (objectTypeField == 'tables') {
266
- if (newobj == null)
267
- plan.dropTable(oldobj);
268
- else
303
+ if (newobj == null) {
304
+ if (!opts.noDropTable) {
305
+ plan.dropTable(oldobj);
306
+ }
307
+ }
308
+ else {
269
309
  planAlterTable(plan, oldobj, newobj, opts);
310
+ }
270
311
  }
271
312
  else {
272
- if (newobj == null)
273
- plan.dropSqlObject(oldobj);
274
- else if (newobj.createSql != oldobj.createSql) {
313
+ if (newobj == null) {
314
+ if (!opts.noDropSqlObject) {
315
+ plan.dropSqlObject(oldobj);
316
+ }
317
+ }
318
+ else if (!testEqualSqlObjects(oldobj.createSql, newobj.createSql, opts)) {
275
319
  plan.recreates.sqlObjects += 1;
276
- plan.dropSqlObject(oldobj);
320
+ if (!opts.noDropSqlObject) {
321
+ plan.dropSqlObject(oldobj);
322
+ }
277
323
  plan.createSqlObject(newobj);
278
324
  }
279
325
  }
@@ -281,11 +327,11 @@ function createAlterDatabasePlan(oldDb, newDb, opts, db, driver) {
281
327
  for (const newobj of newDb[objectTypeField] || []) {
282
328
  const oldobj = (oldDb[objectTypeField] || []).find(x => x.pairingId == newobj.pairingId);
283
329
  if (objectTypeField == 'tables') {
284
- if (newobj == null)
330
+ if (oldobj == null)
285
331
  plan.createTable(newobj);
286
332
  }
287
333
  else {
288
- if (newobj == null)
334
+ if (oldobj == null)
289
335
  plan.createSqlObject(newobj);
290
336
  }
291
337
  }
@@ -295,6 +341,9 @@ function createAlterDatabasePlan(oldDb, newDb, opts, db, driver) {
295
341
  }
296
342
  exports.createAlterDatabasePlan = createAlterDatabasePlan;
297
343
  function getAlterTableScript(oldTable, newTable, opts, db, driver) {
344
+ if ((!oldTable && !newTable) || !driver) {
345
+ return { sql: '', recreates: [] };
346
+ }
298
347
  const plan = createAlterTablePlan(oldTable, newTable, opts, db, driver);
299
348
  const dmp = driver.createDumper();
300
349
  if (!driver.dialect.disableExplicitTransaction)
@@ -319,6 +368,42 @@ function getAlterDatabaseScript(oldDb, newDb, opts, db, driver) {
319
368
  return {
320
369
  sql: dmp.s,
321
370
  recreates: plan.recreates,
371
+ isEmpty: plan.operations.length == 0,
322
372
  };
323
373
  }
324
374
  exports.getAlterDatabaseScript = getAlterDatabaseScript;
375
+ function matchPairedObjects(db1, db2, opts) {
376
+ if (!db1 || !db2)
377
+ return null;
378
+ const res = (0, cloneDeep_1.default)(db2);
379
+ for (const objectTypeField of ['tables', 'views', 'procedures', 'matviews', 'functions']) {
380
+ for (const obj2 of res[objectTypeField] || []) {
381
+ const obj1 = db1[objectTypeField].find(x => testEqualFullNames(x, obj2, opts));
382
+ if (obj1) {
383
+ obj2.pairingId = obj1.pairingId;
384
+ if (objectTypeField == 'tables') {
385
+ for (const col2 of obj2.columns) {
386
+ const col1 = obj1.columns.find(x => testEqualNames(x.columnName, col2.columnName, opts));
387
+ if (col1)
388
+ col2.pairingId = col1.pairingId;
389
+ }
390
+ for (const fk2 of obj2.foreignKeys) {
391
+ const fk1 = obj1.foreignKeys.find(x => testEqualNames(x.refTableName, fk2.refTableName, opts) &&
392
+ (0, isEqual_1.default)(x.columns.map(y => (0, pick_1.default)(y, ['columnName', 'refColumnName'])), fk2.columns.map(y => (0, pick_1.default)(y, ['columnName', 'refColumnName']))));
393
+ if (fk1)
394
+ fk2.pairingId = fk1.pairingId;
395
+ }
396
+ }
397
+ }
398
+ }
399
+ }
400
+ return res;
401
+ }
402
+ exports.matchPairedObjects = matchPairedObjects;
403
+ exports.modelCompareDbDiffOptions = {
404
+ ignoreCase: true,
405
+ schemaMode: 'ignore',
406
+ ignoreConstraintNames: true,
407
+ ignoreForeignKeyActions: true,
408
+ ignoreDataTypes: true,
409
+ };
package/lib/index.d.ts CHANGED
@@ -13,4 +13,6 @@ export * from './settingsExtractors';
13
13
  export * from './filterName';
14
14
  export * from './diffTools';
15
15
  export * from './schemaEditorTools';
16
+ export * from './yamlModelConv';
16
17
  export * from './stringTools';
18
+ export * from './computeDiffRows';
package/lib/index.js CHANGED
@@ -25,4 +25,6 @@ __exportStar(require("./settingsExtractors"), exports);
25
25
  __exportStar(require("./filterName"), exports);
26
26
  __exportStar(require("./diffTools"), exports);
27
27
  __exportStar(require("./schemaEditorTools"), exports);
28
+ __exportStar(require("./yamlModelConv"), exports);
28
29
  __exportStar(require("./stringTools"), exports);
30
+ __exportStar(require("./computeDiffRows"), exports);
@@ -0,0 +1,25 @@
1
+ import { TableInfo, DatabaseInfo } from 'dbgate-types';
2
+ export interface ColumnInfoYaml {
3
+ name: string;
4
+ type: string;
5
+ notNull?: boolean;
6
+ autoIncrement?: boolean;
7
+ references?: string;
8
+ primaryKey?: boolean;
9
+ }
10
+ export interface DatabaseModelFile {
11
+ name: string;
12
+ text: string;
13
+ json: {};
14
+ }
15
+ export interface TableInfoYaml {
16
+ name: string;
17
+ columns: ColumnInfoYaml[];
18
+ primaryKey?: string[];
19
+ }
20
+ export interface ForeignKeyInfoYaml {
21
+ deleteAction?: string;
22
+ }
23
+ export declare function tableInfoToYaml(table: TableInfo): TableInfoYaml;
24
+ export declare function tableInfoFromYaml(table: TableInfoYaml, allTables: TableInfoYaml[]): TableInfo;
25
+ export declare function databaseInfoFromYamlModel(files: DatabaseModelFile[]): DatabaseInfo;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.databaseInfoFromYamlModel = exports.tableInfoFromYaml = exports.tableInfoToYaml = void 0;
7
+ const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
8
+ const compact_1 = __importDefault(require("lodash/compact"));
9
+ const DatabaseAnalyser_1 = require("./DatabaseAnalyser");
10
+ // function foreignKeyInfoToYaml() {}
11
+ function columnInfoToYaml(column, table) {
12
+ const res = {
13
+ name: column.columnName,
14
+ type: column.dataType,
15
+ };
16
+ if (column.autoIncrement)
17
+ res.autoIncrement = true;
18
+ if (column.notNull)
19
+ res.notNull = true;
20
+ const fk = table.foreignKeys &&
21
+ table.foreignKeys.find(fk => fk.columns.length == 1 && fk.columns[0].columnName == column.columnName);
22
+ if (fk &&
23
+ // fk.refSchemaName == table.schemaName &&
24
+ (!fk.deleteAction || fk.deleteAction == 'NO ACTION') &&
25
+ (!fk.updateAction || fk.updateAction == 'NO ACTION')) {
26
+ res.references = fk.refTableName;
27
+ fk['_dumped'] = true;
28
+ }
29
+ // if (table.primaryKey && table.primaryKey.columns.length == 1) {
30
+ // table.primaryKey['_dumped'] = true;
31
+ // res.primaryKey = true;
32
+ // }
33
+ return res;
34
+ }
35
+ function columnInfoFromYaml(column, table) {
36
+ const res = {
37
+ pureName: table.name,
38
+ columnName: column.name,
39
+ dataType: column.type,
40
+ autoIncrement: column.autoIncrement,
41
+ notNull: column.notNull || (table.primaryKey && table.primaryKey.includes(column.name)),
42
+ };
43
+ return res;
44
+ }
45
+ function tableInfoToYaml(table) {
46
+ const tableCopy = (0, cloneDeep_1.default)(table);
47
+ const res = {
48
+ name: tableCopy.pureName,
49
+ // schema: tableCopy.schemaName,
50
+ columns: tableCopy.columns.map(c => columnInfoToYaml(c, tableCopy)),
51
+ };
52
+ if (tableCopy.primaryKey && !tableCopy.primaryKey['_dumped']) {
53
+ res.primaryKey = tableCopy.primaryKey.columns.map(x => x.columnName);
54
+ }
55
+ // const foreignKeys = (tableCopy.foreignKeys || []).filter(x => !x['_dumped']).map(foreignKeyInfoToYaml);
56
+ return res;
57
+ }
58
+ exports.tableInfoToYaml = tableInfoToYaml;
59
+ function convertForeignKeyFromYaml(col, table, allTables) {
60
+ const refTable = allTables.find(x => x.name == col.references);
61
+ if (!refTable || !refTable.primaryKey)
62
+ return null;
63
+ return {
64
+ constraintType: 'foreignKey',
65
+ pureName: table.name,
66
+ refTableName: col.references,
67
+ columns: [
68
+ {
69
+ columnName: col.name,
70
+ refColumnName: refTable.primaryKey[0],
71
+ },
72
+ ],
73
+ };
74
+ }
75
+ function tableInfoFromYaml(table, allTables) {
76
+ const res = {
77
+ pureName: table.name,
78
+ columns: table.columns.map(c => columnInfoFromYaml(c, table)),
79
+ foreignKeys: (0, compact_1.default)(table.columns.filter(x => x.references).map(col => convertForeignKeyFromYaml(col, table, allTables))),
80
+ };
81
+ if (table.primaryKey) {
82
+ res.primaryKey = {
83
+ pureName: table.name,
84
+ constraintType: 'primaryKey',
85
+ columns: table.primaryKey.map(columnName => ({ columnName })),
86
+ };
87
+ }
88
+ return res;
89
+ }
90
+ exports.tableInfoFromYaml = tableInfoFromYaml;
91
+ function databaseInfoFromYamlModel(files) {
92
+ const model = DatabaseAnalyser_1.DatabaseAnalyser.createEmptyStructure();
93
+ const tablesYaml = [];
94
+ for (const file of files) {
95
+ if (file.name.endsWith('.table.yaml') || file.name.endsWith('.sql')) {
96
+ if (file.name.endsWith('.table.yaml')) {
97
+ tablesYaml.push(file.json);
98
+ }
99
+ if (file.name.endsWith('.view.sql')) {
100
+ model.views.push({
101
+ pureName: file.name.slice(0, -'.view.sql'.length),
102
+ createSql: file.text,
103
+ columns: [],
104
+ });
105
+ }
106
+ if (file.name.endsWith('.matview.sql')) {
107
+ model.matviews.push({
108
+ pureName: file.name.slice(0, -'.matview.sql'.length),
109
+ createSql: file.text,
110
+ columns: [],
111
+ });
112
+ }
113
+ if (file.name.endsWith('.proc.sql')) {
114
+ model.procedures.push({
115
+ pureName: file.name.slice(0, -'.proc.sql'.length),
116
+ createSql: file.text,
117
+ });
118
+ }
119
+ if (file.name.endsWith('.func.sql')) {
120
+ model.functions.push({
121
+ pureName: file.name.slice(0, -'.func.sql'.length),
122
+ createSql: file.text,
123
+ });
124
+ }
125
+ if (file.name.endsWith('.trigger.sql')) {
126
+ model.triggers.push({
127
+ pureName: file.name.slice(0, -'.trigger.sql'.length),
128
+ createSql: file.text,
129
+ });
130
+ }
131
+ }
132
+ }
133
+ model.tables = tablesYaml.map(table => tableInfoFromYaml(table, tablesYaml));
134
+ return model;
135
+ }
136
+ exports.databaseInfoFromYamlModel = databaseInfoFromYamlModel;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.3.4",
2
+ "version": "4.4.1",
3
3
  "name": "dbgate-tools",
4
4
  "main": "lib/index.js",
5
5
  "typings": "lib/index.d.ts",
@@ -25,14 +25,14 @@
25
25
  ],
26
26
  "devDependencies": {
27
27
  "@types/node": "^13.7.0",
28
- "dbgate-types": "^4.3.4",
28
+ "dbgate-types": "^4.4.1",
29
29
  "jest": "^24.9.0",
30
30
  "ts-jest": "^25.2.1",
31
31
  "typescript": "^4.4.3"
32
32
  },
33
33
  "dependencies": {
34
34
  "lodash": "^4.17.21",
35
- "dbgate-query-splitter": "^4.3.4",
35
+ "dbgate-query-splitter": "^4.4.1",
36
36
  "uuid": "^3.4.0"
37
37
  }
38
38
  }