mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 18:26:00 +00:00
added plugins
This commit is contained in:
21
plugins/dbgate-plugin-mysql/LICENSE
Normal file
21
plugins/dbgate-plugin-mysql/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Jan Prochazka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
6
plugins/dbgate-plugin-mysql/README.md
Normal file
6
plugins/dbgate-plugin-mysql/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
[](https://github.com/prettier/prettier)
|
||||
[](https://www.npmjs.com/package/dbgate-plugin-mysql)
|
||||
|
||||
# dbgate-plugin-mysql
|
||||
|
||||
Use DbGate for install of this plugin
|
||||
47
plugins/dbgate-plugin-mysql/icon.svg
Normal file
47
plugins/dbgate-plugin-mysql/icon.svg
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path style="fill:#F2F2F2;" d="M512,256c0,36.937-7.826,72.056-21.911,103.769c-2.288,5.183-4.765,10.271-7.387,15.266
|
||||
C439.85,456.474,354.408,512,256,512c-103.685,0-192.972-61.639-233.221-150.277c-7.022-15.464-12.549-31.744-16.394-48.661
|
||||
C2.205,294.713,0,275.613,0,256C0,114.615,114.615,0,256,0c115.012,0,212.302,75.839,244.611,180.245
|
||||
c1.024,3.323,1.985,6.687,2.884,10.073v0.01c1.672,6.332,3.114,12.737,4.305,19.247c0.554,3.03,1.055,6.071,1.494,9.132
|
||||
c0.303,2.048,0.585,4.096,0.825,6.165c0.46,3.699,0.825,7.429,1.118,11.18C511.739,242.636,512,249.292,512,256z"/>
|
||||
<g>
|
||||
<path style="fill:#5181A2;" d="M367.961,77.614c0,0,7.42,7.632,8.691,13.355c0,0,5.3-4.028,1.696-10.387
|
||||
C374.744,74.222,367.961,77.614,367.961,77.614z"/>
|
||||
<path style="fill:#5181A2;" d="M503.495,190.318c-6.666-3.354-12.727-5.339-17.471-6.51c-6.416-1.588-11.609-6.311-13.855-12.528
|
||||
c-2.884-7.973-8.255-20.888-18.139-40.458c-19.508-38.578-41.127-50.448-55.327-60.416c-9.32-6.541-21.567-7.868-28.881-8.014
|
||||
c-3.688-0.073-7.241-1.421-10.073-3.793c-7.45-6.269-22.1-13.636-26.76-10.877c-5.726,3.396,0,11.243,8.046,21.201
|
||||
c8.056,9.968,9.759,20.992,15.475,35.83c2.644,6.844,5.831,10.982,8.485,13.448c2.779,2.591,3.688,6.614,2.215,10.104
|
||||
c-5.371,12.696-7.168,28.776-4.127,43.645c3.605,17.596,11.87,17.387,11.87,17.387c-0.846-23.531,11.452-33.708,11.452-33.708
|
||||
c11.233,34.335,36.885,59.988,36.885,59.988c-20.773-7.419-36.247-41.336-36.247-41.336s-1.693,1.693-3.177,12.936
|
||||
c-1.484,11.233-15.057,18.014-25.015-6.572c-9.968-24.597,2.967-55.547,2.967-55.547c-5.726-2.968-17.596-30.312-17.596-34.555
|
||||
c0-4.232-4.671-13.563-10.386-19.289c-5.726-5.716-16.959-22.256-3.814-29.884c13.134-7.638,39.215,12.925,39.215,12.925
|
||||
c41.127-1.484,83.519,48.546,90.3,66.351c6.781,17.805,25.861,53.426,25.861,53.426c5.413,1.641,10.501,3.762,15.214,6.175
|
||||
C501.635,183.568,502.596,186.932,503.495,190.318z"/>
|
||||
<path style="fill:#5181A2;" d="M511.237,236.053c-0.209-0.094-0.408-0.199-0.606-0.293c-13.145-6.363-18.442-19.717-18.442-19.717
|
||||
c3.406-3.406,9.446-5.36,15.611-6.468c0.554,3.03,1.055,6.071,1.494,9.132c-2.299,0.449-3.751,0.721-3.751,0.721
|
||||
c0,1.296,1.808,3.229,4.577,5.444C510.579,228.571,510.945,232.302,511.237,236.053z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path style="fill:#FFA600;" d="M133.371,374.659H113.34c0,0,0-45.777-6.677-100.153L75.18,374.659H58.964l-31.953-99.673
|
||||
c0,0-3.72,29.288-4.232,86.737c-7.022-15.464-12.549-31.744-16.394-48.661c1.473-21.264,3.542-44.44,6.322-63.352h26.07
|
||||
l29.727,92.526l31.483-92.526h23.845C123.831,249.71,133.371,331.264,133.371,374.659z"/>
|
||||
<path style="fill:#FFA600;" d="M144.423,283.452l20.668-0.318c0,0,23.212,69.637,20.987,78.222c0,0,14.945-41.655,18.761-78.222
|
||||
h20.35c0,0-10.812,73.771-43.88,112.564c-12.375,14.517-26.262,14.695-38.799,9.236v-11.143c0,0,17.177,6.042,26.081-6.678
|
||||
c7.168-10.24,0.322-27.898-5.938-46.671C161.135,335.896,144.423,283.452,144.423,283.452z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path style="fill:#5181A2;" d="M237.908,365.491l5.405-10.812c0,0,37.204,16.534,46.742,0c9.539-16.534,0.318-25.438-18.442-35.296
|
||||
c-18.76-9.858-38.475-22.258-29.89-49.287c8.586-27.028,54.056-25.12,67.093-12.083l-4.451,10.493c0,0-33.705-14.309-42.926,4.451
|
||||
c-9.221,18.76,15.263,27.664,20.987,30.208c5.724,2.544,37.521,15.58,34.024,38.793s-17.807,37.521-50.558,34.659
|
||||
C254.124,373.758,247.447,371.85,237.908,365.491z"/>
|
||||
<path style="fill:#5181A2;" d="M405.452,367c14.054-11.295,20.083-31.389,20.083-54.293c0-35.328-10.167-63.969-50.333-63.969
|
||||
c-34.586,0-50.333,28.641-50.333,63.969s11.515,63.969,50.333,63.969c5.622,0,10.721-0.7,15.318-2.017l29.905,17.544l7.314-14.315
|
||||
L405.452,367z M375.202,361.357c-22.507,0-29.184-21.786-29.184-48.65c0-26.875,6.363-48.65,29.184-48.65
|
||||
c23.28,0,29.174,21.776,29.174,48.65C404.375,339.571,398.482,361.357,375.202,361.357z"/>
|
||||
<path style="fill:#5181A2;" d="M490.088,359.769c-2.288,5.183-4.765,10.271-7.387,15.266h-39.382V250.378h19.717v109.39H490.088z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
39
plugins/dbgate-plugin-mysql/package.json
Normal file
39
plugins/dbgate-plugin-mysql/package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "dbgate-plugin-mysql",
|
||||
"main": "dist/backend.js",
|
||||
"version": "1.2.2",
|
||||
"homepage": "https://github.com/dbgate/dbgate-plugin-mysql",
|
||||
"description": "MySQL connect plugin for DbGate",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dbgate/dbgate-plugin-mysql.git"
|
||||
},
|
||||
"funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur",
|
||||
"author": "Jan Prochazka",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"sql",
|
||||
"dbgate",
|
||||
"dbgateplugin",
|
||||
"mysql"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build:frontend": "webpack --config webpack-frontend.config",
|
||||
"build:backend": "webpack --config webpack-backend.config.js",
|
||||
"build": "yarn build:frontend && yarn build:backend",
|
||||
"plugin": "yarn build && yarn pack && dbgate-plugin dbgate-plugin-mysql",
|
||||
"plugout": "dbgate-plugout dbgate-plugin-mysql",
|
||||
"prepublishOnly": "yarn build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verycrazydog/mysql-parser": "^1.2.0",
|
||||
"dbgate-plugin-tools": "^1.0.4",
|
||||
"dbgate-tools": "^4.0.3-rc.1",
|
||||
"mysql2": "^2.2.5",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
}
|
||||
}
|
||||
9
plugins/dbgate-plugin-mysql/prettier.config.js
Normal file
9
plugins/dbgate-plugin-mysql/prettier.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
tabWidth: 2,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
arrowParen: 'avoid',
|
||||
arrowParens: 'avoid',
|
||||
printWidth: 120,
|
||||
};
|
||||
222
plugins/dbgate-plugin-mysql/src/backend/Analyser.js
Normal file
222
plugins/dbgate-plugin-mysql/src/backend/Analyser.js
Normal file
@@ -0,0 +1,222 @@
|
||||
const fp = require('lodash/fp');
|
||||
const _ = require('lodash');
|
||||
const sql = require('./sql');
|
||||
|
||||
const { DatabaseAnalyser } = require('dbgate-tools');
|
||||
const { isTypeString, isTypeNumeric } = require('dbgate-tools');
|
||||
const { rangeStep } = require('lodash/fp');
|
||||
|
||||
function getColumnInfo({
|
||||
isNullable,
|
||||
extra,
|
||||
columnName,
|
||||
dataType,
|
||||
charMaxLength,
|
||||
numericPrecision,
|
||||
numericScale,
|
||||
defaultValue,
|
||||
}) {
|
||||
let fullDataType = dataType;
|
||||
if (charMaxLength && isTypeString(dataType)) fullDataType = `${dataType}(${charMaxLength})`;
|
||||
if (numericPrecision && numericScale && isTypeNumeric(dataType))
|
||||
fullDataType = `${dataType}(${numericPrecision},${numericScale})`;
|
||||
return {
|
||||
notNull: !isNullable || isNullable == 'NO' || isNullable == 'no',
|
||||
autoIncrement: extra && extra.toLowerCase().includes('auto_increment'),
|
||||
columnName,
|
||||
dataType: fullDataType,
|
||||
defaultValue,
|
||||
};
|
||||
}
|
||||
|
||||
function objectTypeToField(type) {
|
||||
if (type == 'VIEW') return 'views';
|
||||
if (type == 'BASE TABLE') return 'tables';
|
||||
return null;
|
||||
}
|
||||
|
||||
class Analyser extends DatabaseAnalyser {
|
||||
constructor(pool, driver) {
|
||||
super(pool, driver);
|
||||
}
|
||||
|
||||
createQuery(resFileName, typeFields) {
|
||||
let res = sql[resFileName];
|
||||
if (this.singleObjectFilter) {
|
||||
const { typeField, pureName } = this.singleObjectFilter;
|
||||
if (!typeFields || !typeFields.includes(typeField)) return null;
|
||||
res = res.replace('=[OBJECT_NAME_CONDITION]', ` = '${pureName}'`).replace('#DATABASE#', this.pool._database_name);
|
||||
return res;
|
||||
}
|
||||
if (!this.modifications || !typeFields || this.modifications.length == 0) {
|
||||
res = res.replace('=[OBJECT_NAME_CONDITION]', ' is not null');
|
||||
} else {
|
||||
const filterNames = this.modifications
|
||||
.filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change'))
|
||||
.map(x => x.newName && x.newName.pureName)
|
||||
.filter(Boolean);
|
||||
if (filterNames.length == 0) {
|
||||
res = res.replace('=[OBJECT_NAME_CONDITION]', ' IS NULL');
|
||||
} else {
|
||||
res = res.replace('=[OBJECT_NAME_CONDITION]', ` in (${filterNames.map(x => `'${x}'`).join(',')})`);
|
||||
}
|
||||
}
|
||||
res = res.replace('#DATABASE#', this.pool._database_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
getRequestedViewNames(allViewNames) {
|
||||
if (this.singleObjectFilter) {
|
||||
const { typeField, pureName } = this.singleObjectFilter;
|
||||
if (typeField == 'views') return [pureName];
|
||||
}
|
||||
if (this.modifications) {
|
||||
return this.modifications.filter(x => x.objectTypeField == 'views').map(x => x.newName.pureName);
|
||||
}
|
||||
return allViewNames;
|
||||
}
|
||||
|
||||
async getViewTexts(allViewNames) {
|
||||
const res = {};
|
||||
for (const viewName of this.getRequestedViewNames(allViewNames)) {
|
||||
const resp = await this.driver.query(this.pool, `SHOW CREATE VIEW \`${viewName}\``);
|
||||
res[viewName] = resp.rows[0]['Create View'];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async _runAnalysis() {
|
||||
const tables = await this.driver.query(this.pool, this.createQuery('tables', ['tables']));
|
||||
const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables', 'views']));
|
||||
const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables']));
|
||||
const fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables']));
|
||||
const views = await this.driver.query(this.pool, this.createQuery('views', ['views']));
|
||||
const programmables = await this.driver.query(
|
||||
this.pool,
|
||||
this.createQuery('programmables', ['procedures', 'functions'])
|
||||
);
|
||||
|
||||
const viewTexts = await this.getViewTexts(views.rows.map(x => x.pureName));
|
||||
|
||||
return this.mergeAnalyseResult({
|
||||
tables: tables.rows.map(table => ({
|
||||
...table,
|
||||
objectId: table.pureName,
|
||||
columns: columns.rows.filter(col => col.pureName == table.pureName).map(getColumnInfo),
|
||||
primaryKey: DatabaseAnalyser.extractPrimaryKeys(table, pkColumns.rows),
|
||||
foreignKeys: DatabaseAnalyser.extractForeignKeys(table, fkColumns.rows),
|
||||
})),
|
||||
views: views.rows.map(view => ({
|
||||
...view,
|
||||
objectId: view.pureName,
|
||||
columns: columns.rows.filter(col => col.pureName == view.pureName).map(getColumnInfo),
|
||||
createSql: viewTexts[view.pureName],
|
||||
requiresFormat: true,
|
||||
})),
|
||||
procedures: programmables.rows
|
||||
.filter(x => x.objectType == 'PROCEDURE')
|
||||
.map(fp.omit(['objectType']))
|
||||
.map(x => ({ ...x, objectId: x.pureName })),
|
||||
functions: programmables.rows
|
||||
.filter(x => x.objectType == 'FUNCTION')
|
||||
.map(fp.omit(['objectType']))
|
||||
.map(x => ({ ...x, objectId: x.pureName })),
|
||||
});
|
||||
}
|
||||
|
||||
getDeletedObjectsForField(nameArray, objectTypeField) {
|
||||
return this.structure[objectTypeField]
|
||||
.filter(x => !nameArray.includes(x.pureName))
|
||||
.map(x => ({
|
||||
oldName: _.pick(x, ['pureName']),
|
||||
action: 'remove',
|
||||
objectTypeField,
|
||||
objectId: x.pureName,
|
||||
}));
|
||||
}
|
||||
|
||||
getDeletedObjects(nameArray) {
|
||||
return [
|
||||
...this.getDeletedObjectsForField(nameArray, 'tables'),
|
||||
...this.getDeletedObjectsForField(nameArray, 'views'),
|
||||
...this.getDeletedObjectsForField(nameArray, 'procedures'),
|
||||
...this.getDeletedObjectsForField(nameArray, 'functions'),
|
||||
...this.getDeletedObjectsForField(nameArray, 'triggers'),
|
||||
];
|
||||
}
|
||||
|
||||
async getModifications() {
|
||||
const tableModificationsQueryData = await this.driver.query(this.pool, this.createQuery('tableModifications'));
|
||||
const procedureModificationsQueryData = await this.driver.query(
|
||||
this.pool,
|
||||
this.createQuery('procedureModifications')
|
||||
);
|
||||
const functionModificationsQueryData = await this.driver.query(
|
||||
this.pool,
|
||||
this.createQuery('functionModifications')
|
||||
);
|
||||
|
||||
const allModifications = _.compact([
|
||||
...tableModificationsQueryData.rows.map(x => {
|
||||
if (x.objectType == 'BASE TABLE') return { ...x, objectTypeField: 'tables' };
|
||||
if (x.objectType == 'VIEW') return { ...x, objectTypeField: 'views' };
|
||||
return null;
|
||||
}),
|
||||
...procedureModificationsQueryData.rows.map(x => ({
|
||||
objectTypeField: 'procedures',
|
||||
modifyDate: x.Modified,
|
||||
pureName: x.Name,
|
||||
})),
|
||||
...functionModificationsQueryData.rows.map(x => ({
|
||||
objectTypeField: 'functions',
|
||||
modifyDate: x.Modified,
|
||||
pureName: x.Name,
|
||||
})),
|
||||
]);
|
||||
|
||||
// console.log('allModifications', allModifications);
|
||||
// console.log(
|
||||
// 'DATES',
|
||||
// this.structure.procedures.map((x) => x.modifyDate)
|
||||
// );
|
||||
// console.log('MOD - SRC', modifications);
|
||||
// console.log(
|
||||
// 'MODs',
|
||||
// this.structure.tables.map((x) => x.modifyDate)
|
||||
// );
|
||||
const modifications = allModifications.map(x => {
|
||||
const { objectType, modifyDate, pureName } = x;
|
||||
const field = objectTypeToField(objectType);
|
||||
|
||||
if (!field || !this.structure[field]) return null;
|
||||
// @ts-ignore
|
||||
const obj = this.structure[field].find(x => x.pureName == pureName);
|
||||
|
||||
// object not modified
|
||||
if (obj && Math.abs(new Date(modifyDate).getTime() - new Date(obj.modifyDate).getTime()) < 1000) return null;
|
||||
|
||||
// console.log('MODIFICATION OF ', field, pureName, modifyDate, obj.modifyDate);
|
||||
|
||||
/** @type {import('dbgate-types').DatabaseModification} */
|
||||
const action = obj
|
||||
? {
|
||||
newName: { pureName },
|
||||
oldName: _.pick(obj, ['pureName']),
|
||||
action: 'change',
|
||||
objectTypeField: field,
|
||||
objectId: pureName,
|
||||
}
|
||||
: {
|
||||
newName: { pureName },
|
||||
action: 'add',
|
||||
objectTypeField: field,
|
||||
objectId: pureName,
|
||||
};
|
||||
return action;
|
||||
});
|
||||
|
||||
return [..._.compact(modifications), ...this.getDeletedObjects([...allModifications.map(x => x.pureName)])];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Analyser;
|
||||
186
plugins/dbgate-plugin-mysql/src/backend/driver.js
Normal file
186
plugins/dbgate-plugin-mysql/src/backend/driver.js
Normal file
@@ -0,0 +1,186 @@
|
||||
const _ = require('lodash');
|
||||
const stream = require('stream');
|
||||
const driverBase = require('../frontend/driver');
|
||||
const Analyser = require('./Analyser');
|
||||
const mysql2 = require('mysql2');
|
||||
const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools');
|
||||
const mysqlSplitter = require('@verycrazydog/mysql-parser');
|
||||
|
||||
function extractColumns(fields) {
|
||||
if (fields) {
|
||||
const res = fields.map(col => ({
|
||||
columnName: col.name,
|
||||
}));
|
||||
makeUniqueColumnNames(res);
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function zipDataRow(rowArray, columns) {
|
||||
return _.zipObject(
|
||||
columns.map(x => x.columnName),
|
||||
rowArray
|
||||
);
|
||||
}
|
||||
|
||||
async function runQueryItem(connection, sql) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.query(sql, function (error, results, fields) {
|
||||
if (error) reject(error);
|
||||
const columns = extractColumns(fields);
|
||||
resolve({ rows: results && columns && results.map && results.map(row => zipDataRow(row, columns)), columns });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function runStreamItem(connection, sql, options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = connection.query(sql);
|
||||
let columns = [];
|
||||
|
||||
// const handleInfo = (info) => {
|
||||
// const { message, lineNumber, procName } = info;
|
||||
// options.info({
|
||||
// message,
|
||||
// line: lineNumber,
|
||||
// procedure: procName,
|
||||
// time: new Date(),
|
||||
// severity: 'info',
|
||||
// });
|
||||
// };
|
||||
|
||||
const handleEnd = () => {
|
||||
resolve();
|
||||
};
|
||||
|
||||
const handleRow = row => {
|
||||
if (row && row.constructor && row.constructor.name == 'OkPacket') {
|
||||
options.info({
|
||||
message: `${row.affectedRows} rows affected`,
|
||||
time: new Date(),
|
||||
severity: 'info',
|
||||
});
|
||||
} else {
|
||||
if (columns) {
|
||||
options.row(zipDataRow(row, columns));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleFields = fields => {
|
||||
columns = extractColumns(fields);
|
||||
if (columns) options.recordset(columns);
|
||||
};
|
||||
|
||||
const handleError = error => {
|
||||
console.log('ERROR', error);
|
||||
const { message, lineNumber, procName } = error;
|
||||
options.info({
|
||||
message,
|
||||
line: lineNumber,
|
||||
procedure: procName,
|
||||
time: new Date(),
|
||||
severity: 'error',
|
||||
});
|
||||
};
|
||||
|
||||
query.on('error', handleError).on('fields', handleFields).on('result', handleRow).on('end', handleEnd);
|
||||
});
|
||||
}
|
||||
|
||||
/** @type {import('dbgate-types').EngineDriver} */
|
||||
const driver = {
|
||||
...driverBase,
|
||||
analyserClass: Analyser,
|
||||
|
||||
async connect({ server, port, user, password, database, ssl }) {
|
||||
const connection = mysql2.createConnection({
|
||||
host: server,
|
||||
port,
|
||||
user,
|
||||
password,
|
||||
database,
|
||||
ssl,
|
||||
rowsAsArray: true,
|
||||
supportBigNumbers: true,
|
||||
bigNumberStrings: true,
|
||||
// TODO: test following options
|
||||
// multipleStatements: true,
|
||||
// dateStrings: true,
|
||||
});
|
||||
connection._database_name = database;
|
||||
return connection;
|
||||
},
|
||||
async query(connection, sql) {
|
||||
if (sql == null) {
|
||||
return {
|
||||
rows: [],
|
||||
columns: [],
|
||||
};
|
||||
}
|
||||
|
||||
const sqlSplitted = mysqlSplitter.split(sql);
|
||||
let res = {
|
||||
rows: [],
|
||||
columns: [],
|
||||
};
|
||||
for (const sqlItem of sqlSplitted) {
|
||||
const resultItem = await runQueryItem(connection, sqlItem);
|
||||
if (resultItem.rows && resultItem.columns && resultItem.columns.length > 0) {
|
||||
res = resultItem;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
async stream(connection, sql, options) {
|
||||
const sqlSplitted = mysqlSplitter.split(sql);
|
||||
|
||||
for (const sqlItem of sqlSplitted) {
|
||||
await runStreamItem(connection, sqlItem, options);
|
||||
}
|
||||
|
||||
options.done();
|
||||
},
|
||||
async readQuery(connection, sql, structure) {
|
||||
const query = connection.query(sql);
|
||||
|
||||
const pass = new stream.PassThrough({
|
||||
objectMode: true,
|
||||
highWaterMark: 100,
|
||||
});
|
||||
|
||||
let columns = [];
|
||||
query
|
||||
.on('error', err => {
|
||||
console.error(err);
|
||||
pass.end();
|
||||
})
|
||||
.on('fields', fields => {
|
||||
columns = extractColumns(fields);
|
||||
pass.write({
|
||||
__isStreamHeader: true,
|
||||
...(structure || { columns }),
|
||||
});
|
||||
})
|
||||
.on('result', row => pass.write(zipDataRow(row, columns)))
|
||||
.on('end', () => pass.end());
|
||||
|
||||
return pass;
|
||||
},
|
||||
async getVersion(connection) {
|
||||
const { rows } = await this.query(connection, "show variables like 'version'");
|
||||
const version = rows[0].Value;
|
||||
return { version };
|
||||
},
|
||||
async listDatabases(connection) {
|
||||
const { rows } = await this.query(connection, 'show databases');
|
||||
return rows.map(x => ({ name: x.Database }));
|
||||
},
|
||||
async writeTable(pool, name, options) {
|
||||
// @ts-ignore
|
||||
return createBulkInsertStreamBase(this, stream, pool, name, options);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = driver;
|
||||
6
plugins/dbgate-plugin-mysql/src/backend/index.js
Normal file
6
plugins/dbgate-plugin-mysql/src/backend/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const driver = require('./driver');
|
||||
|
||||
module.exports = {
|
||||
packageName: 'dbgate-plugin-mysql',
|
||||
driver,
|
||||
};
|
||||
15
plugins/dbgate-plugin-mysql/src/backend/sql/columns.js
Normal file
15
plugins/dbgate-plugin-mysql/src/backend/sql/columns.js
Normal file
@@ -0,0 +1,15 @@
|
||||
module.exports = `
|
||||
select
|
||||
TABLE_NAME as pureName,
|
||||
COLUMN_NAME as columnName,
|
||||
IS_NULLABLE as isNullable,
|
||||
DATA_TYPE as dataType,
|
||||
CHARACTER_MAXIMUM_LENGTH as charMaxLength,
|
||||
NUMERIC_PRECISION as numericPrecision,
|
||||
NUMERIC_SCALE as numericScale,
|
||||
COLUMN_DEFAULT as defaultValue,
|
||||
EXTRA as extra
|
||||
from INFORMATION_SCHEMA.COLUMNS
|
||||
where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =[OBJECT_NAME_CONDITION]
|
||||
order by ORDINAL_POSITION
|
||||
`;
|
||||
17
plugins/dbgate-plugin-mysql/src/backend/sql/foreignKeys.js
Normal file
17
plugins/dbgate-plugin-mysql/src/backend/sql/foreignKeys.js
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = `
|
||||
select
|
||||
REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME as constraintName,
|
||||
REFERENTIAL_CONSTRAINTS.TABLE_NAME as pureName,
|
||||
REFERENTIAL_CONSTRAINTS.UPDATE_RULE as updateAction,
|
||||
REFERENTIAL_CONSTRAINTS.DELETE_RULE as deleteAction,
|
||||
REFERENTIAL_CONSTRAINTS.REFERENCED_TABLE_NAME as refTableName,
|
||||
KEY_COLUMN_USAGE.COLUMN_NAME as columnName,
|
||||
KEY_COLUMN_USAGE.REFERENCED_COLUMN_NAME as refColumnName
|
||||
from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
|
||||
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
on REFERENTIAL_CONSTRAINTS.TABLE_NAME = KEY_COLUMN_USAGE.TABLE_NAME
|
||||
and REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME = KEY_COLUMN_USAGE.CONSTRAINT_NAME
|
||||
and REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA = KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA
|
||||
where REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and REFERENTIAL_CONSTRAINTS.TABLE_NAME =[OBJECT_NAME_CONDITION]
|
||||
order by KEY_COLUMN_USAGE.ORDINAL_POSITION
|
||||
`;
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = `
|
||||
SHOW FUNCTION STATUS WHERE Db = '#DATABASE#'
|
||||
`;
|
||||
21
plugins/dbgate-plugin-mysql/src/backend/sql/index.js
Normal file
21
plugins/dbgate-plugin-mysql/src/backend/sql/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const columns = require('./columns');
|
||||
const tables = require('./tables');
|
||||
const primaryKeys = require('./primaryKeys');
|
||||
const foreignKeys = require('./foreignKeys');
|
||||
const tableModifications = require('./tableModifications');
|
||||
const views = require('./views');
|
||||
const programmables = require('./programmables');
|
||||
const procedureModifications = require('./procedureModifications');
|
||||
const functionModifications = require('./functionModifications');
|
||||
|
||||
module.exports = {
|
||||
columns,
|
||||
tables,
|
||||
primaryKeys,
|
||||
foreignKeys,
|
||||
tableModifications,
|
||||
views,
|
||||
programmables,
|
||||
procedureModifications,
|
||||
functionModifications,
|
||||
};
|
||||
12
plugins/dbgate-plugin-mysql/src/backend/sql/primaryKeys.js
Normal file
12
plugins/dbgate-plugin-mysql/src/backend/sql/primaryKeys.js
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = `select
|
||||
TABLE_CONSTRAINTS.CONSTRAINT_NAME as constraintName,
|
||||
TABLE_CONSTRAINTS.TABLE_NAME as pureName,
|
||||
KEY_COLUMN_USAGE.COLUMN_NAME as columnName
|
||||
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
||||
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
on TABLE_CONSTRAINTS.TABLE_NAME = KEY_COLUMN_USAGE.TABLE_NAME
|
||||
and TABLE_CONSTRAINTS.CONSTRAINT_NAME = KEY_COLUMN_USAGE.CONSTRAINT_NAME
|
||||
and TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA
|
||||
where TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and TABLE_CONSTRAINTS.TABLE_NAME =[OBJECT_NAME_CONDITION] AND TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'PRIMARY KEY'
|
||||
order by KEY_COLUMN_USAGE.ORDINAL_POSITION
|
||||
`;
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = `
|
||||
SHOW PROCEDURE STATUS WHERE Db = '#DATABASE#'
|
||||
`;
|
||||
@@ -0,0 +1,9 @@
|
||||
module.exports = `
|
||||
select
|
||||
ROUTINE_NAME as pureName,
|
||||
ROUTINE_TYPE as objectType,
|
||||
COALESCE(LAST_ALTERED, CREATED) as modifyDate,
|
||||
ROUTINE_DEFINITION as createSql
|
||||
from information_schema.routines
|
||||
where ROUTINE_SCHEMA = '#DATABASE#' and ROUTINE_NAME =[OBJECT_NAME_CONDITION]
|
||||
`;
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = `
|
||||
select
|
||||
TABLE_NAME as pureName,
|
||||
TABLE_TYPE as objectType,
|
||||
case when ENGINE='InnoDB' then CREATE_TIME else coalesce(UPDATE_TIME, CREATE_TIME) end as modifyDate
|
||||
from information_schema.tables
|
||||
where TABLE_SCHEMA = '#DATABASE#'
|
||||
`;
|
||||
7
plugins/dbgate-plugin-mysql/src/backend/sql/tables.js
Normal file
7
plugins/dbgate-plugin-mysql/src/backend/sql/tables.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = `
|
||||
select
|
||||
TABLE_NAME as pureName,
|
||||
case when ENGINE='InnoDB' then CREATE_TIME else coalesce(UPDATE_TIME, CREATE_TIME) end as modifyDate
|
||||
from information_schema.tables
|
||||
where TABLE_SCHEMA = '#DATABASE#' and TABLE_TYPE='BASE TABLE' and TABLE_NAME =[OBJECT_NAME_CONDITION];
|
||||
`;
|
||||
7
plugins/dbgate-plugin-mysql/src/backend/sql/views.js
Normal file
7
plugins/dbgate-plugin-mysql/src/backend/sql/views.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = `
|
||||
select
|
||||
TABLE_NAME as pureName,
|
||||
coalesce(UPDATE_TIME, CREATE_TIME) as modifyDate
|
||||
from information_schema.tables
|
||||
where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =[OBJECT_NAME_CONDITION] and TABLE_TYPE = 'VIEW';
|
||||
`;
|
||||
68
plugins/dbgate-plugin-mysql/src/frontend/Dumper.js
Normal file
68
plugins/dbgate-plugin-mysql/src/frontend/Dumper.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const { SqlDumper } = require('dbgate-tools');
|
||||
|
||||
class Dumper extends SqlDumper {
|
||||
/** @param type {import('dbgate-types').TransformType} */
|
||||
transform(type, dumpExpr) {
|
||||
switch (type) {
|
||||
case 'GROUP:YEAR':
|
||||
case 'YEAR':
|
||||
this.put('^year(%c)', dumpExpr);
|
||||
break;
|
||||
case 'MONTH':
|
||||
this.put('^month(%c)', dumpExpr);
|
||||
break;
|
||||
case 'DAY':
|
||||
this.put('^day(%c)', dumpExpr);
|
||||
break;
|
||||
case 'GROUP:MONTH':
|
||||
this.put("^date_format(%c, '%s')", dumpExpr, '%Y-%m');
|
||||
break;
|
||||
case 'GROUP:DAY':
|
||||
this.put("^date_format(%c, '%s')", dumpExpr, '%Y-%m-%d');
|
||||
break;
|
||||
default:
|
||||
dumpExpr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
renameTable(obj, newName) {
|
||||
this.putCmd('^rename ^table %f ^to %i', obj, newName);
|
||||
}
|
||||
|
||||
changeColumn(oldcol, newcol, constraints) {
|
||||
this.put('^alter ^table %f ^change ^column %i %i ', oldcol, oldcol.columnName, newcol.columnName);
|
||||
this.columnDefinition(newcol, true, true, true);
|
||||
this.inlineConstraints(constraints);
|
||||
this.endCommand();
|
||||
}
|
||||
|
||||
renameColumn(column, newcol) {
|
||||
this.changeColumn(
|
||||
column,
|
||||
{
|
||||
...column,
|
||||
columnName: newcol,
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
enableConstraints(table, enabled) {
|
||||
this.putCmd('^set FOREIGN_KEY_CHECKS = %s', enabled ? '1' : '0');
|
||||
}
|
||||
|
||||
comment(value) {
|
||||
this.put('/* %s */', value);
|
||||
}
|
||||
|
||||
beginTransaction() {
|
||||
this.putCmd('^start ^transaction');
|
||||
}
|
||||
|
||||
selectTableIntoNewTable(sourceName, targetName) {
|
||||
this.putCmd('^create ^table %f (^select * ^from %f)', targetName, sourceName);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Dumper;
|
||||
27
plugins/dbgate-plugin-mysql/src/frontend/driver.js
Normal file
27
plugins/dbgate-plugin-mysql/src/frontend/driver.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { driverBase } = require('dbgate-tools');
|
||||
const Dumper = require('./Dumper');
|
||||
|
||||
/** @type {import('dbgate-types').SqlDialect} */
|
||||
const dialect = {
|
||||
rangeSelect: true,
|
||||
stringEscapeChar: '\\',
|
||||
fallbackDataType: 'longtext',
|
||||
enableConstraintsPerTable: false,
|
||||
anonymousPrimaryKey: true,
|
||||
explicitDropConstraint: true,
|
||||
quoteIdentifier(s) {
|
||||
return '`' + s + '`';
|
||||
},
|
||||
};
|
||||
|
||||
/** @type {import('dbgate-types').EngineDriver} */
|
||||
const driver = {
|
||||
...driverBase,
|
||||
dumperClass: Dumper,
|
||||
dialect,
|
||||
engine: 'mysql@dbgate-plugin-mysql',
|
||||
title: 'MySQL / MariaDB',
|
||||
defaultPort: 3306,
|
||||
};
|
||||
|
||||
module.exports = driver;
|
||||
6
plugins/dbgate-plugin-mysql/src/frontend/index.js
Normal file
6
plugins/dbgate-plugin-mysql/src/frontend/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import driver from './driver';
|
||||
|
||||
export default {
|
||||
packageName: 'dbgate-plugin-mysql',
|
||||
driver,
|
||||
};
|
||||
33
plugins/dbgate-plugin-mysql/test/testdb.sql
Normal file
33
plugins/dbgate-plugin-mysql/test/testdb.sql
Normal file
File diff suppressed because one or more lines are too long
23
plugins/dbgate-plugin-mysql/webpack-backend.config.js
Normal file
23
plugins/dbgate-plugin-mysql/webpack-backend.config.js
Normal file
@@ -0,0 +1,23 @@
|
||||
var webpack = require('webpack');
|
||||
var path = require('path');
|
||||
|
||||
var config = {
|
||||
context: __dirname + '/src/backend',
|
||||
|
||||
entry: {
|
||||
app: './index.js',
|
||||
},
|
||||
target: 'node',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'backend.js',
|
||||
libraryTarget: 'commonjs2',
|
||||
},
|
||||
|
||||
// uncomment for disable minimalization
|
||||
// optimization: {
|
||||
// minimize: false,
|
||||
// },
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
24
plugins/dbgate-plugin-mysql/webpack-frontend.config.js
Normal file
24
plugins/dbgate-plugin-mysql/webpack-frontend.config.js
Normal file
@@ -0,0 +1,24 @@
|
||||
var webpack = require("webpack");
|
||||
var path = require("path");
|
||||
|
||||
var config = {
|
||||
context: __dirname + "/src/frontend",
|
||||
|
||||
entry: {
|
||||
app: "./index.js",
|
||||
},
|
||||
target: "web",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "frontend.js",
|
||||
libraryTarget: "var",
|
||||
library: 'plugin',
|
||||
},
|
||||
|
||||
// uncomment for disable minimalization
|
||||
// optimization: {
|
||||
// minimize: false,
|
||||
// },
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
Reference in New Issue
Block a user