create script callable from react

This commit is contained in:
Jan Prochazka
2020-02-03 20:34:38 +01:00
parent 680bed549f
commit 8a85cfe687
14 changed files with 147 additions and 95 deletions

View File

@@ -1,14 +1,19 @@
const mssql = require('mssql'); const mssql = require('mssql');
const mysql = require('mysql'); const mysql = require('mysql');
const pg = require('pg'); const pg = require('pg');
const fs = require('fs-extra');
const path = require('path');
const nativeModules = {
mssql,
mysql,
pg,
fs,
path,
};
function driverConnect(driver, connection) { function driverConnect(driver, connection) {
const driverModules = { return driver.connect(nativeModules, connection);
mssql,
mysql,
pg,
};
return driver.connect(driverModules, connection);
} }
module.exports = driverConnect; module.exports = driverConnect;

View File

@@ -8,8 +8,8 @@ class SqlDumper {
putRaw(text) { putRaw(text) {
this.s += text; this.s += text;
} }
putCmd(text) { putCmd(format, ...args) {
this.putRaw(text); this.put(format, ...args);
this.putRaw(';\n'); this.putRaw(';\n');
} }
putFormattedValue(c, value) { putFormattedValue(c, value) {

View File

@@ -1,7 +1,25 @@
const _ = require("lodash");
const mssql = require('./mssql');
const mysql = require('./mysql');
const postgres = require('./postgres');
const drivers = {
mssql,
mysql,
postgres,
}
/** @return {import('@dbgate/types').EngineDriver} */ /** @return {import('@dbgate/types').EngineDriver} */
function getDriver(connection) { function getDriver(connection) {
const { engine } = connection; if (_.isString(connection)) {
return require(`./${engine}`); return drivers[connection];
}
if (_.isPlainObject(connection)) {
const { engine } = connection;
if (engine) {
return drivers[engine];
}
}
throw new Error(`Cannot extract engine from ${connection}`)
} }
module.exports = getDriver; module.exports = getDriver;

View File

@@ -1,42 +1,49 @@
const fs = require('fs-extra'); const fp = require("lodash/fp");
const fp = require('lodash/fp'); const _ = require("lodash");
const path = require('path');
const _ = require('lodash');
const DatabaseAnalayser = require('../default/DatabaseAnalyser'); const DatabaseAnalayser = require("../default/DatabaseAnalyser");
/** @returns {Promise<string>} */ /** @returns {Promise<string>} */
async function loadQuery(name) { async function loadQuery(pool, name) {
return await fs.readFile(path.join(__dirname, name), 'utf-8'); return await pool._nativeModules.fs.readFile(
pool._nativeModules.path.join(__dirname, name),
"utf-8"
);
} }
const byTableFilter = table => x => x.pureName == table.pureName && x.schemaName == x.schemaName; const byTableFilter = table => x =>
x.pureName == table.pureName && x.schemaName == x.schemaName;
function extractPrimaryKeys(table, pkColumns) { function extractPrimaryKeys(table, pkColumns) {
const filtered = pkColumns.filter(byTableFilter(table)); const filtered = pkColumns.filter(byTableFilter(table));
if (filtered.length == 0) return undefined; if (filtered.length == 0) return undefined;
return { return {
..._.pick(filtered[0], ['constraintName', 'schemaName', 'pureName']), ..._.pick(filtered[0], ["constraintName", "schemaName", "pureName"]),
constraintType: 'primaryKey', constraintType: "primaryKey",
columns: filtered.map(fp.pick('columnName')), columns: filtered.map(fp.pick("columnName"))
}; };
} }
function extractForeignKeys(table, fkColumns) { function extractForeignKeys(table, fkColumns) {
const grouped = _.groupBy(fkColumns.filter(byTableFilter(table)), 'constraintName'); const grouped = _.groupBy(
fkColumns.filter(byTableFilter(table)),
"constraintName"
);
return _.keys(grouped).map(constraintName => ({ return _.keys(grouped).map(constraintName => ({
constraintName, constraintName,
constraintType: 'foreignKey', constraintType: "foreignKey",
..._.pick(grouped[constraintName][0], [ ..._.pick(grouped[constraintName][0], [
'constraintName', "constraintName",
'schemaName', "schemaName",
'pureName', "pureName",
'refSchemaName', "refSchemaName",
'refTableName', "refTableName",
'updateAction', "updateAction",
'deleteAction', "deleteAction"
]), ]),
columns: grouped[constraintName].map(fp.pick(['columnName', 'refColumnName'])), columns: grouped[constraintName].map(
fp.pick(["columnName", "refColumnName"])
)
})); }));
} }
@@ -53,15 +60,27 @@ class MsSqlAnalyser extends DatabaseAnalayser {
functions = false, functions = false,
triggers = false triggers = false
) { ) {
let res = await loadQuery(resFileName); let res = await loadQuery(this.pool, resFileName);
res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null'); res = res.replace("=[OBJECT_ID_CONDITION]", " is not null");
return res; return res;
} }
async runAnalysis() { async runAnalysis() {
const tables = await this.driver.query(this.pool, await this.createQuery('tables.sql')); const tables = await this.driver.query(
const columns = await this.driver.query(this.pool, await this.createQuery('columns.sql')); this.pool,
const pkColumns = await this.driver.query(this.pool, await this.createQuery('primary_keys.sql')); await this.createQuery("tables.sql")
const fkColumns = await this.driver.query(this.pool, await this.createQuery('foreign_keys.sql')); );
const columns = await this.driver.query(
this.pool,
await this.createQuery("columns.sql")
);
const pkColumns = await this.driver.query(
this.pool,
await this.createQuery("primary_keys.sql")
);
const fkColumns = await this.driver.query(
this.pool,
await this.createQuery("foreign_keys.sql")
);
this.result.tables = tables.rows.map(table => ({ this.result.tables = tables.rows.map(table => ({
...table, ...table,
@@ -70,10 +89,10 @@ class MsSqlAnalyser extends DatabaseAnalayser {
.map(({ isNullable, isIdentity, ...col }) => ({ .map(({ isNullable, isIdentity, ...col }) => ({
...col, ...col,
notNull: !isNullable, notNull: !isNullable,
autoIncrement: !!isIdentity, autoIncrement: !!isIdentity
})), })),
primaryKey: extractPrimaryKeys(table, pkColumns.rows), primaryKey: extractPrimaryKeys(table, pkColumns.rows),
foreignKeys: extractForeignKeys(table, fkColumns.rows), foreignKeys: extractForeignKeys(table, fkColumns.rows)
})); }));
} }
} }

View File

@@ -12,14 +12,15 @@ const dialect = {
/** @type {import('@dbgate/types').EngineDriver} */ /** @type {import('@dbgate/types').EngineDriver} */
const driver = { const driver = {
async connect({ mssql }, { server, port, user, password, database }) { async connect(nativeModules, { server, port, user, password, database }) {
const pool = await mssql.connect({ const pool = await nativeModules.mssql.connect({
server, server,
port, port,
user, user,
password, password,
database database
}); });
pool._nativeModules = nativeModules;
return pool; return pool;
}, },
async query(pool, sql) { async query(pool, sql) {

View File

@@ -1,13 +1,14 @@
const fs = require('fs-extra'); const fp = require("lodash/fp");
const fp = require('lodash/fp'); const _ = require("lodash");
const path = require('path');
const _ = require('lodash');
const DatabaseAnalayser = require('../default/DatabaseAnalyser'); const DatabaseAnalayser = require("../default/DatabaseAnalyser");
/** @returns {Promise<string>} */ /** @returns {Promise<string>} */
async function loadQuery(name) { async function loadQuery(pool, name) {
return await fs.readFile(path.join(__dirname, name), 'utf-8'); return await pool._nativeModules.fs.readFile(
pool._nativeModules.path.join(__dirname, name),
"utf-8"
);
} }
class MySqlAnalyser extends DatabaseAnalayser { class MySqlAnalyser extends DatabaseAnalayser {
@@ -23,14 +24,20 @@ class MySqlAnalyser extends DatabaseAnalayser {
functions = false, functions = false,
triggers = false triggers = false
) { ) {
let res = await loadQuery(resFileName); let res = await loadQuery(this.pool, resFileName);
res = res.replace('=[OBJECT_NAME_CONDITION]', ' is not null'); res = res.replace("=[OBJECT_NAME_CONDITION]", " is not null");
res = res.replace('#DATABASE#', this.pool._database_name); res = res.replace("#DATABASE#", this.pool._database_name);
return res; return res;
} }
async runAnalysis() { async runAnalysis() {
const tables = await this.driver.query(this.pool, await this.createQuery('tables.sql')); const tables = await this.driver.query(
const columns = await this.driver.query(this.pool, await this.createQuery('columns.sql')); this.pool,
await this.createQuery("tables.sql")
);
const columns = await this.driver.query(
this.pool,
await this.createQuery("columns.sql")
);
// const pkColumns = await this.driver.query(this.pool, await this.createQuery('primary_keys.sql')); // const pkColumns = await this.driver.query(this.pool, await this.createQuery('primary_keys.sql'));
// const fkColumns = await this.driver.query(this.pool, await this.createQuery('foreign_keys.sql')); // const fkColumns = await this.driver.query(this.pool, await this.createQuery('foreign_keys.sql'));
@@ -41,9 +48,9 @@ class MySqlAnalyser extends DatabaseAnalayser {
.map(({ isNullable, extra, ...col }) => ({ .map(({ isNullable, extra, ...col }) => ({
...col, ...col,
notNull: !isNullable, notNull: !isNullable,
autoIncrement: extra && extra.toLowerCase().includes('auto_increment'), autoIncrement: extra && extra.toLowerCase().includes("auto_increment")
})), })),
foreignKeys: [], foreignKeys: []
// primaryKey: extractPrimaryKeys(table, pkColumns.rows), // primaryKey: extractPrimaryKeys(table, pkColumns.rows),
// foreignKeys: extractForeignKeys(table, fkColumns.rows), // foreignKeys: extractForeignKeys(table, fkColumns.rows),
})); }));

View File

@@ -11,8 +11,8 @@ const dialect = {
/** @type {import('@dbgate/types').EngineDriver} */ /** @type {import('@dbgate/types').EngineDriver} */
const driver = { const driver = {
async connect({ mysql }, { server, port, user, password, database }) { async connect(nativeModules, { server, port, user, password, database }) {
const connection = mysql.createConnection({ const connection = nativeModules.mysql.createConnection({
host: server, host: server,
port, port,
user, user,
@@ -20,6 +20,7 @@ const driver = {
database database
}); });
connection._database_name = database; connection._database_name = database;
connection._nativeModules = nativeModules;
return connection; return connection;
}, },
async query(connection, sql) { async query(connection, sql) {

View File

@@ -10,8 +10,6 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"dependencies": { "dependencies": {
"fs-extra": "^8.1.0", "lodash": "^4.17.15"
"lodash": "^4.17.15",
"path": "^0.12.7"
} }
} }

View File

@@ -1,13 +1,14 @@
const fs = require('fs-extra'); const fp = require("lodash/fp");
const fp = require('lodash/fp'); const _ = require("lodash");
const path = require('path');
const _ = require('lodash');
const DatabaseAnalayser = require('../default/DatabaseAnalyser'); const DatabaseAnalayser = require("../default/DatabaseAnalyser");
/** @returns {Promise<string>} */ /** @returns {Promise<string>} */
async function loadQuery(name) { async function loadQuery(pool, name) {
return await fs.readFile(path.join(__dirname, name), 'utf-8'); return await pool._nativeModules.fs.readFile(
pool._nativeModules.path.join(__dirname, name),
"utf-8"
);
} }
class MySqlAnalyser extends DatabaseAnalayser { class MySqlAnalyser extends DatabaseAnalayser {
@@ -23,25 +24,34 @@ class MySqlAnalyser extends DatabaseAnalayser {
functions = false, functions = false,
triggers = false triggers = false
) { ) {
let res = await loadQuery(resFileName); let res = await loadQuery(this.pool, resFileName);
res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null'); res = res.replace("=[OBJECT_ID_CONDITION]", " is not null");
return res; return res;
} }
async runAnalysis() { async runAnalysis() {
const tables = await this.driver.query(this.pool, await this.createQuery('table_modifications.psql')); const tables = await this.driver.query(
const columns = await this.driver.query(this.pool, await this.createQuery('columns.psql')); this.pool,
await this.createQuery("table_modifications.psql")
);
const columns = await this.driver.query(
this.pool,
await this.createQuery("columns.psql")
);
// const pkColumns = await this.driver.query(this.pool, await this.createQuery('primary_keys.sql')); // const pkColumns = await this.driver.query(this.pool, await this.createQuery('primary_keys.sql'));
// const fkColumns = await this.driver.query(this.pool, await this.createQuery('foreign_keys.sql')); // const fkColumns = await this.driver.query(this.pool, await this.createQuery('foreign_keys.sql'));
this.result.tables = tables.rows.map(table => ({ this.result.tables = tables.rows.map(table => ({
...table, ...table,
columns: columns.rows columns: columns.rows
.filter(col => col.pureName == table.pureName && col.schemaName == table.schemaName) .filter(
col =>
col.pureName == table.pureName && col.schemaName == table.schemaName
)
.map(({ isNullable, ...col }) => ({ .map(({ isNullable, ...col }) => ({
...col, ...col,
notNull: !isNullable, notNull: !isNullable
})), })),
foreignKeys: [], foreignKeys: []
// primaryKey: extractPrimaryKeys(table, pkColumns.rows), // primaryKey: extractPrimaryKeys(table, pkColumns.rows),
// foreignKeys: extractForeignKeys(table, fkColumns.rows), // foreignKeys: extractForeignKeys(table, fkColumns.rows),
})); }));

View File

@@ -11,9 +11,10 @@ const dialect = {
/** @type {import('@dbgate/types').EngineDriver} */ /** @type {import('@dbgate/types').EngineDriver} */
const driver = { const driver = {
async connect({pg}, { server, port, user, password, database }) { async connect(nativeModules, { server, port, user, password, database }) {
const client = new pg.Client({ host: server, port, user, password, database: database || 'postgres' }); const client = new nativeModules.pg.Client({ host: server, port, user, password, database: database || 'postgres' });
await client.connect(); await client.connect();
client._nativeModules = nativeModules;
return client; return client;
}, },
async query(client, sql) { async query(client, sql) {

View File

@@ -4,7 +4,7 @@ import { SqlDumper } from "./dumper";
import { DatabaseInfo } from "./dbinfo"; import { DatabaseInfo } from "./dbinfo";
export interface EngineDriver { export interface EngineDriver {
connect(driverModules, { server, port, user, password, database }): any; connect(nativeModules, { server, port, user, password, database }): any;
query(pool: any, sql: string): Promise<QueryResult>; query(pool: any, sql: string): Promise<QueryResult>;
getVersion(pool: any): Promise<{ version: string }>; getVersion(pool: any): Promise<{ version: string }>;
listDatabases( listDatabases(

View File

@@ -21,7 +21,8 @@
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"socket.io-client": "^2.3.0", "socket.io-client": "^2.3.0",
"styled-components": "^4.4.1", "styled-components": "^4.4.1",
"uuid": "^3.4.0" "uuid": "^3.4.0",
"@dbgate/engines": "^0.1.0"
}, },
"scripts": { "scripts": {
"start": "cross-env PORT=5000 react-scripts start", "start": "cross-env PORT=5000 react-scripts start",

View File

@@ -4,6 +4,7 @@ import styled from 'styled-components';
import theme from '../theme'; import theme from '../theme';
import AceEditor from 'react-ace'; import AceEditor from 'react-ace';
import useDimensions from '../utility/useDimensions'; import useDimensions from '../utility/useDimensions';
import engines from '@dbgate/engines';
const Wrapper = styled.div` const Wrapper = styled.div`
position: absolute; position: absolute;
@@ -23,6 +24,11 @@ export default function TableCreateScriptTab({ conid, database, schemaName, pure
params: { conid, database, schemaName, pureName }, params: { conid, database, schemaName, pureName },
}); });
/** @type {import('@dbgate/types').EngineDriver} */
const driver = engines('mssql');
const dumper = driver.createDumper();
dumper.putCmd('^select * ^from %f', { schemaName, pureName });
return ( return (
<Wrapper ref={containerRef}> <Wrapper ref={containerRef}>
<AceEditor <AceEditor
@@ -31,7 +37,7 @@ export default function TableCreateScriptTab({ conid, database, schemaName, pure
// onChange={onChange} // onChange={onChange}
name="UNIQUE_ID_OF_DIV" name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }} editorProps={{ $blockScrolling: true }}
value={sql} value={dumper.s}
readOnly readOnly
fontSize="11pt" fontSize="11pt"
width={`${width}px`} width={`${width}px`}

View File

@@ -8503,14 +8503,6 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
path@^0.12.7:
version "0.12.7"
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=
dependencies:
process "^0.11.1"
util "^0.10.3"
pbkdf2@^3.0.3: pbkdf2@^3.0.3:
version "3.0.17" version "3.0.17"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
@@ -9416,7 +9408,7 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@^0.11.1, process@^0.11.10: process@^0.11.10:
version "0.11.10" version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
@@ -11820,13 +11812,6 @@ util@0.10.3:
dependencies: dependencies:
inherits "2.0.1" inherits "2.0.1"
util@^0.10.3:
version "0.10.4"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
dependencies:
inherits "2.0.3"
util@^0.11.0: util@^0.11.0:
version "0.11.1" version "0.11.1"
resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"