nmig/migration/fmtp/ViewGenerator.js

178 lines
7.8 KiB
JavaScript

/*
* 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 fs = require('fs');
const path = require('path');
const log = require('./Logger');
const generateError = require('./ErrorGenerator');
const migrationStateManager = require('./MigrationStateManager');
const getBuffer = +process.version.split('.')[0].slice(1) < 6
? require('./OldBuffer')
: require('./NewBuffer');
/**
* Attempts to convert MySQL view to PostgreSQL view.
*
* @param {String} schema
* @param {String} viewName
* @param {String} mysqlViewCode
*
* @returns {String}
*/
const generateView = (schema, viewName, mysqlViewCode) => {
mysqlViewCode = mysqlViewCode.split('`').join('"');
const queryStart = mysqlViewCode.indexOf('AS');
mysqlViewCode = mysqlViewCode.slice(queryStart);
const arrMysqlViewCode = mysqlViewCode.split(' ');
for (let i = 0; i < arrMysqlViewCode.length; ++i) {
if (
arrMysqlViewCode[i].toLowerCase() === 'from'
|| arrMysqlViewCode[i].toLowerCase() === 'join'
&& i + 1 < arrMysqlViewCode.length
) {
arrMysqlViewCode[i + 1] = '"' + schema + '".' + arrMysqlViewCode[i + 1];
}
}
return 'CREATE OR REPLACE VIEW "' + schema + '"."' + viewName + '" ' + arrMysqlViewCode.join(' ') + ';';
}
/**
* Writes a log, containing a view code.
*
* @param {Conversion} self
* @param {String} viewName
* @param {String} sql
*
* @returns {undefined}
*/
const logNotCreatedView = (self, viewName, sql) => {
fs.stat(self._notCreatedViewsPath, (directoryDoesNotExist, stat) => {
if (directoryDoesNotExist) {
fs.mkdir(self._notCreatedViewsPath, self._0777, e => {
if (e) {
log(self, '\t--[logNotCreatedView] ' + e);
} else {
log(self, '\t--[logNotCreatedView] "not_created_views" directory is created...');
// "not_created_views" directory is created. Can write the log...
fs.open(path.join(self._notCreatedViewsPath, viewName + '.sql'), 'w', self._0777, (error, fd) => {
if (error) {
log(self, error);
} else {
let buffer = getBuffer(sql, self._encoding);
fs.write(fd, buffer, 0, buffer.length, null, () => {
buffer = null;
fs.close(fd, () => {
// Each async function MUST have a callback (according to Node.js >= 7).
});
});
}
});
}
});
} else if (!stat.isDirectory()) {
log(self, '\t--[logNotCreatedView] Cannot write the log due to unexpected error');
} else {
// "not_created_views" directory already exists. Can write the log...
fs.open(path.join(self._notCreatedViewsPath, viewName + '.sql'), 'w', self._0777, (error, fd) => {
if (error) {
log(self, error);
} else {
let buffer = getBuffer(sql, self._encoding);
fs.write(fd, buffer, 0, buffer.length, null, () => {
buffer = null;
fs.close(fd, () => {
// Each async function MUST have a callback (according to Node.js >= 7).
});
});
}
});
}
});
}
/**
* Attempts to convert MySQL view to PostgreSQL view.
*
* @param {Conversion} self
*
* @returns {Promise}
*/
module.exports = self => {
return migrationStateManager.get(self, 'views_loaded').then(hasViewsLoaded => {
return new Promise(resolve => {
const createViewPromises = [];
if (!hasViewsLoaded) {
for (let i = 0; i < self._viewsToMigrate.length; ++i) {
createViewPromises.push(
new Promise(resolveProcessView2 => {
self._mysql.getConnection((error, connection) => {
if (error) {
// The connection is undefined.
generateError(self, '\t--[processView] Cannot connect to MySQL server...\n' + error);
resolveProcessView2();
} else {
let sql = 'SHOW CREATE VIEW `' + self._viewsToMigrate[i] + '`;';
connection.query(sql, (strErr, rows) => {
connection.release();
if (strErr) {
generateError(self, '\t--[processView] ' + strErr, sql);
resolveProcessView2();
} else {
self._pg.connect((error, client, done) => {
if (error) {
generateError(self, '\t--[processView] Cannot connect to PostgreSQL server...');
resolveProcessView2();
} else {
sql = generateView(self._schema, self._viewsToMigrate[i], rows[0]['Create View']);
rows = null;
client.query(sql, err => {
done();
if (err) {
generateError(self, '\t--[processView] ' + err, sql);
logNotCreatedView(self, self._viewsToMigrate[i], sql);
resolveProcessView2();
} else {
log(self, '\t--[processView] View "' + self._schema + '"."' + self._viewsToMigrate[i] + '" is created...');
resolveProcessView2();
}
});
}
});
}
});
}
});
})
);
}
}
Promise.all(createViewPromises).then(() => resolve());
});
});
};