migrating to typescript
This commit is contained in:
parent
b83aa19b04
commit
28a5699038
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
* This file is a part of "NMIG" - the database migration tool.
|
||||
*
|
||||
* Copyright (C) 2016 - present, Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (please see the "LICENSE.md" file).
|
||||
* If not, see <http://www.gnu.org/licenses/gpl.txt>.
|
||||
*
|
||||
* @author Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const log = require('./Logger');
|
||||
const generateError = require('./ErrorGenerator');
|
||||
const migrationStateManager = require('./MigrationStateManager');
|
||||
const extraConfigProcessor = require('./ExtraConfigProcessor');
|
||||
|
||||
/**
|
||||
* Creates foreign keys for given table.
|
||||
*
|
||||
* @param {Conversion} self
|
||||
* @param {String} tableName
|
||||
* @param {Array} rows
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const processForeignKeyWorker = (self, tableName, rows) => {
|
||||
return new Promise(resolve => {
|
||||
const constraintsPromises = [];
|
||||
const objConstraints = Object.create(null);
|
||||
const originalTableName = extraConfigProcessor.getTableName(self, tableName, true);
|
||||
|
||||
for (let i = 0; i < rows.length; ++i) {
|
||||
const currentColumnName = extraConfigProcessor.getColumnName(self, originalTableName, rows[i].COLUMN_NAME, false);
|
||||
const currentReferencedTableName = extraConfigProcessor.getTableName(self, rows[i].REFERENCED_TABLE_NAME, false);
|
||||
const originalReferencedTableName = extraConfigProcessor.getTableName(self, rows[i].REFERENCED_TABLE_NAME, true);
|
||||
const currentReferencedColumnName = extraConfigProcessor.getColumnName(
|
||||
self,
|
||||
originalReferencedTableName,
|
||||
rows[i].REFERENCED_COLUMN_NAME,
|
||||
false
|
||||
);
|
||||
|
||||
if (rows[i].CONSTRAINT_NAME in objConstraints) {
|
||||
objConstraints[rows[i].CONSTRAINT_NAME].column_name.push('"' + currentColumnName + '"');
|
||||
objConstraints[rows[i].CONSTRAINT_NAME].referenced_column_name.push('"' + currentReferencedColumnName + '"');
|
||||
} else {
|
||||
objConstraints[rows[i].CONSTRAINT_NAME] = Object.create(null);
|
||||
objConstraints[rows[i].CONSTRAINT_NAME].column_name = ['"' + currentColumnName + '"'];
|
||||
objConstraints[rows[i].CONSTRAINT_NAME].referenced_column_name = ['"' + currentReferencedColumnName + '"'];
|
||||
objConstraints[rows[i].CONSTRAINT_NAME].referenced_table_name = currentReferencedTableName;
|
||||
objConstraints[rows[i].CONSTRAINT_NAME].update_rule = rows[i].UPDATE_RULE;
|
||||
objConstraints[rows[i].CONSTRAINT_NAME].delete_rule = rows[i].DELETE_RULE;
|
||||
}
|
||||
}
|
||||
|
||||
rows = null;
|
||||
|
||||
for (const attr in objConstraints) {
|
||||
constraintsPromises.push(
|
||||
new Promise(resolveConstraintPromise => {
|
||||
self._pg.connect((error, client, done) => {
|
||||
if (error) {
|
||||
objConstraints[attr] = null;
|
||||
generateError(self, '\t--[processForeignKeyWorker] Cannot connect to PostgreSQL server...');
|
||||
resolveConstraintPromise();
|
||||
} else {
|
||||
const sql = 'ALTER TABLE "' + self._schema + '"."' + tableName + '" ADD FOREIGN KEY ('
|
||||
+ objConstraints[attr].column_name.join(',') + ') REFERENCES "' + self._schema + '"."'
|
||||
+ objConstraints[attr].referenced_table_name + '" (' + objConstraints[attr].referenced_column_name.join(',')
|
||||
+ ') ON UPDATE ' + objConstraints[attr].update_rule + ' ON DELETE ' + objConstraints[attr].delete_rule + ';';
|
||||
|
||||
objConstraints[attr] = null;
|
||||
client.query(sql, err => {
|
||||
done();
|
||||
|
||||
if (err) {
|
||||
generateError(self, '\t--[processForeignKeyWorker] ' + err, sql);
|
||||
resolveConstraintPromise();
|
||||
} else {
|
||||
resolveConstraintPromise();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Promise.all(constraintsPromises).then(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a process of foreign keys creation.
|
||||
*
|
||||
* @param {Conversion} self
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
module.exports = self => {
|
||||
return migrationStateManager.get(self, 'foreign_keys_loaded').then(isForeignKeysProcessed => {
|
||||
return new Promise(resolve => {
|
||||
const fkPromises = [];
|
||||
|
||||
if (!isForeignKeysProcessed) {
|
||||
for (let i = 0; i < self._tablesToMigrate.length; ++i) {
|
||||
const tableName = self._tablesToMigrate[i];
|
||||
log(self, '\t--[processForeignKey] Search foreign keys for table "' + self._schema + '"."' + tableName + '"...');
|
||||
fkPromises.push(
|
||||
new Promise(fkResolve => {
|
||||
self._mysql.getConnection((error, connection) => {
|
||||
if (error) {
|
||||
// The connection is undefined.
|
||||
generateError(self, '\t--[processForeignKey] Cannot connect to MySQL server...\n' + error);
|
||||
fkResolve();
|
||||
} else {
|
||||
const sql = "SELECT cols.COLUMN_NAME, refs.REFERENCED_TABLE_NAME, refs.REFERENCED_COLUMN_NAME, "
|
||||
+ "cRefs.UPDATE_RULE, cRefs.DELETE_RULE, cRefs.CONSTRAINT_NAME "
|
||||
+ "FROM INFORMATION_SCHEMA.`COLUMNS` AS cols "
|
||||
+ "INNER JOIN INFORMATION_SCHEMA.`KEY_COLUMN_USAGE` AS refs "
|
||||
+ "ON refs.TABLE_SCHEMA = cols.TABLE_SCHEMA "
|
||||
+ "AND refs.REFERENCED_TABLE_SCHEMA = cols.TABLE_SCHEMA "
|
||||
+ "AND refs.TABLE_NAME = cols.TABLE_NAME "
|
||||
+ "AND refs.COLUMN_NAME = cols.COLUMN_NAME "
|
||||
+ "LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS cRefs "
|
||||
+ "ON cRefs.CONSTRAINT_SCHEMA = cols.TABLE_SCHEMA "
|
||||
+ "AND cRefs.CONSTRAINT_NAME = refs.CONSTRAINT_NAME "
|
||||
+ "LEFT JOIN INFORMATION_SCHEMA.`KEY_COLUMN_USAGE` AS links "
|
||||
+ "ON links.TABLE_SCHEMA = cols.TABLE_SCHEMA "
|
||||
+ "AND links.REFERENCED_TABLE_SCHEMA = cols.TABLE_SCHEMA "
|
||||
+ "AND links.REFERENCED_TABLE_NAME = cols.TABLE_NAME "
|
||||
+ "AND links.REFERENCED_COLUMN_NAME = cols.COLUMN_NAME "
|
||||
+ "LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS cLinks "
|
||||
+ "ON cLinks.CONSTRAINT_SCHEMA = cols.TABLE_SCHEMA "
|
||||
+ "AND cLinks.CONSTRAINT_NAME = links.CONSTRAINT_NAME "
|
||||
+ "WHERE cols.TABLE_SCHEMA = '" + self._mySqlDbName + "' "
|
||||
+ "AND cols.TABLE_NAME = '" + extraConfigProcessor.getTableName(self, tableName, true) + "';";
|
||||
|
||||
connection.query(sql, (err, rows) => {
|
||||
connection.release();
|
||||
|
||||
if (err) {
|
||||
generateError(self, '\t--[processForeignKey] ' + err, sql);
|
||||
}
|
||||
|
||||
const extraRows = extraConfigProcessor.parseForeignKeys(self, tableName);
|
||||
const fullRows = (rows || []).concat(extraRows); // Prevent failure if "rows" is undefined.
|
||||
processForeignKeyWorker(self, tableName, fullRows).then(() => {
|
||||
log(self, '\t--[processForeignKey] Foreign keys for table "' + self._schema + '"."' + tableName + '" are set...');
|
||||
fkResolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all(fkPromises).then(() => resolve());
|
||||
});
|
||||
});
|
||||
};
|
120
src/ForeignKeyProcessor.ts
Normal file
120
src/ForeignKeyProcessor.ts
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* This file is a part of "NMIG" - the database migration tool.
|
||||
*
|
||||
* Copyright (C) 2016 - present, Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (please see the "LICENSE.md" file).
|
||||
* If not, see <http://www.gnu.org/licenses/gpl.txt>.
|
||||
*
|
||||
* @author Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*/
|
||||
import migrationStateManager from './MigrationStateManager';
|
||||
import log from './Logger';
|
||||
import Conversion from './Conversion';
|
||||
import DBAccess from './DBAccess';
|
||||
import DBVendors from './DBVendors';
|
||||
import DBAccessQueryResult from './DBAccessQueryResult';
|
||||
import * as extraConfigProcessor from './ExtraConfigProcessor';
|
||||
|
||||
/**
|
||||
* Creates foreign keys for given table.
|
||||
*/
|
||||
async function processForeignKeyWorker(conversion: Conversion, dbAccess: DBAccess, tableName: string, rows: any[]): Promise<void> {
|
||||
const objConstraints: any = Object.create(null);
|
||||
const originalTableName: string = extraConfigProcessor.getTableName(conversion, tableName, true);
|
||||
const logTitle: string = 'processForeignKeyWorker';
|
||||
|
||||
rows.forEach((row: any) => {
|
||||
const currentColumnName: string = extraConfigProcessor.getColumnName(conversion, originalTableName, row.COLUMN_NAME, false);
|
||||
const currentReferencedTableName: string = extraConfigProcessor.getTableName(conversion, row.REFERENCED_TABLE_NAME, false);
|
||||
const originalReferencedTableName: string = extraConfigProcessor.getTableName(conversion, row.REFERENCED_TABLE_NAME, true);
|
||||
const currentReferencedColumnName: string = extraConfigProcessor.getColumnName(
|
||||
conversion,
|
||||
originalReferencedTableName,
|
||||
row.REFERENCED_COLUMN_NAME,
|
||||
false
|
||||
);
|
||||
|
||||
if (row.CONSTRAINT_NAME in objConstraints) {
|
||||
objConstraints[row.CONSTRAINT_NAME].column_name.push(`"${ currentColumnName }"`);
|
||||
objConstraints[row.CONSTRAINT_NAME].referenced_column_name.push(`"${ currentReferencedColumnName }"`);
|
||||
return;
|
||||
}
|
||||
|
||||
objConstraints[row.CONSTRAINT_NAME] = Object.create(null);
|
||||
objConstraints[row.CONSTRAINT_NAME].column_name = [`"${ currentColumnName }"`];
|
||||
objConstraints[row.CONSTRAINT_NAME].referenced_column_name = [`"${ currentReferencedColumnName }"`];
|
||||
objConstraints[row.CONSTRAINT_NAME].referenced_table_name = currentReferencedTableName;
|
||||
objConstraints[row.CONSTRAINT_NAME].update_rule = row.UPDATE_RULE;
|
||||
objConstraints[row.CONSTRAINT_NAME].delete_rule = row.DELETE_RULE;
|
||||
});
|
||||
|
||||
const constraintsPromises: Promise<void>[] = Object.keys(objConstraints).map(async (attr: string) => {
|
||||
const sql: string = `ALTER TABLE "${ conversion._schema }"."${ tableName }"
|
||||
ADD FOREIGN KEY (${ objConstraints[attr].column_name.join(',') })
|
||||
REFERENCES "${ conversion._schema }"."${ objConstraints[attr].referenced_table_name }"
|
||||
(${ objConstraints[attr].referenced_column_name.join(',') })
|
||||
ON UPDATE ${ objConstraints[attr].update_rule }
|
||||
ON DELETE ${ objConstraints[attr].delete_rule };`;
|
||||
|
||||
await dbAccess.query(logTitle, sql, DBVendors.PG, false, false);
|
||||
});
|
||||
|
||||
await Promise.all(constraintsPromises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a process of foreign keys creation.
|
||||
*/
|
||||
export default async function(conversion: Conversion): Promise<void> {
|
||||
const logTitle: string = 'processForeignKey';
|
||||
const isForeignKeysProcessed: boolean = await migrationStateManager.get(conversion, 'foreign_keys_loaded');
|
||||
|
||||
if (isForeignKeysProcessed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fkPromises: Promise<void>[] = conversion._tablesToMigrate.map(async (tableName: string) => {
|
||||
log(conversion, `\t--[${ logTitle }] Search foreign keys for table "${ conversion._schema }"."${ tableName }"...`);
|
||||
const sql: string = `SELECT cols.COLUMN_NAME, refs.REFERENCED_TABLE_NAME, refs.REFERENCED_COLUMN_NAME,
|
||||
cRefs.UPDATE_RULE, cRefs.DELETE_RULE, cRefs.CONSTRAINT_NAME
|
||||
FROM INFORMATION_SCHEMA.\`COLUMNS\` AS cols
|
||||
INNER JOIN INFORMATION_SCHEMA.\`KEY_COLUMN_USAGE\` AS refs
|
||||
ON refs.TABLE_SCHEMA = cols.TABLE_SCHEMA
|
||||
AND refs.REFERENCED_TABLE_SCHEMA = cols.TABLE_SCHEMA
|
||||
AND refs.TABLE_NAME = cols.TABLE_NAME
|
||||
AND refs.COLUMN_NAME = cols.COLUMN_NAME
|
||||
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS cRefs
|
||||
ON cRefs.CONSTRAINT_SCHEMA = cols.TABLE_SCHEMA
|
||||
AND cRefs.CONSTRAINT_NAME = refs.CONSTRAINT_NAME
|
||||
LEFT JOIN INFORMATION_SCHEMA.\`KEY_COLUMN_USAGE\` AS links
|
||||
ON links.TABLE_SCHEMA = cols.TABLE_SCHEMA
|
||||
AND links.REFERENCED_TABLE_SCHEMA = cols.TABLE_SCHEMA
|
||||
AND links.REFERENCED_TABLE_NAME = cols.TABLE_NAME
|
||||
AND links.REFERENCED_COLUMN_NAME = cols.COLUMN_NAME
|
||||
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS cLinks
|
||||
ON cLinks.CONSTRAINT_SCHEMA = cols.TABLE_SCHEMA
|
||||
AND cLinks.CONSTRAINT_NAME = links.CONSTRAINT_NAME
|
||||
WHERE cols.TABLE_SCHEMA = '${ conversion._mySqlDbName }'
|
||||
AND cols.TABLE_NAME = '${ extraConfigProcessor.getTableName(conversion, tableName, true) }';`;
|
||||
|
||||
const dbAccess: DBAccess = new DBAccess(conversion);
|
||||
const result: DBAccessQueryResult = await dbAccess.query(logTitle, sql, DBVendors.MYSQL, false, false);
|
||||
const extraRows: any[] = extraConfigProcessor.parseForeignKeys(conversion, tableName);
|
||||
const fullRows: any[] = (result.data || []).concat(extraRows); // Prevent failure if "result.data" is undefined.
|
||||
await processForeignKeyWorker(conversion, dbAccess, tableName, fullRows);
|
||||
log(conversion, `\t--[${ logTitle }] Foreign keys for table "${ conversion._schema }"."${ tableName }" are set...`);
|
||||
});
|
||||
|
||||
await Promise.all(fkPromises);
|
||||
}
|
|
@ -18,21 +18,29 @@
|
|||
*
|
||||
* @author Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*/
|
||||
'use strict';
|
||||
export default class MessageToMaster {
|
||||
/**
|
||||
* A name of a table, to insert the data into.
|
||||
*/
|
||||
public readonly tableName: string;
|
||||
|
||||
/**
|
||||
* A number of rows, that have already been inserted into given table.
|
||||
*/
|
||||
public rowsInserted: number;
|
||||
|
||||
/**
|
||||
* A number of rows to insert into given table.
|
||||
*/
|
||||
public readonly totalRowsToInsert: number;
|
||||
|
||||
module.exports = class MessageToMaster {
|
||||
/**
|
||||
* Representation of a message of DataLoader process to the master process regarding records,
|
||||
* inserted to specified table.
|
||||
* Constructor.
|
||||
*
|
||||
* @param {String} tableName
|
||||
* @param {Number} rowsInserted
|
||||
* @param {Number} totalRowsToInsert
|
||||
*/
|
||||
constructor(tableName, rowsInserted, totalRowsToInsert) {
|
||||
this.tableName = tableName;
|
||||
this.rowsInserted = rowsInserted;
|
||||
public constructor(tableName: string, rowsInserted: number, totalRowsToInsert: number) {
|
||||
this.tableName = tableName;
|
||||
this.rowsInserted = rowsInserted;
|
||||
this.totalRowsToInsert = totalRowsToInsert;
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue