diff --git a/packages/api/src/utility/driverConnect.js b/packages/api/src/utility/driverConnect.js index 0acd32686..38010a0f5 100644 --- a/packages/api/src/utility/driverConnect.js +++ b/packages/api/src/utility/driverConnect.js @@ -1,14 +1,19 @@ const mssql = require('mssql'); const mysql = require('mysql'); const pg = require('pg'); +const fs = require('fs-extra'); +const path = require('path'); + +const nativeModules = { + mssql, + mysql, + pg, + fs, + path, +}; function driverConnect(driver, connection) { - const driverModules = { - mssql, - mysql, - pg, - }; - return driver.connect(driverModules, connection); + return driver.connect(nativeModules, connection); } module.exports = driverConnect; diff --git a/packages/engines/default/SqlDumper.js b/packages/engines/default/SqlDumper.js index d3d814d18..295dd67e7 100644 --- a/packages/engines/default/SqlDumper.js +++ b/packages/engines/default/SqlDumper.js @@ -8,8 +8,8 @@ class SqlDumper { putRaw(text) { this.s += text; } - putCmd(text) { - this.putRaw(text); + putCmd(format, ...args) { + this.put(format, ...args); this.putRaw(';\n'); } putFormattedValue(c, value) { diff --git a/packages/engines/index.js b/packages/engines/index.js index 42c428bf2..b79cb5866 100644 --- a/packages/engines/index.js +++ b/packages/engines/index.js @@ -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} */ function getDriver(connection) { - const { engine } = connection; - return require(`./${engine}`); + if (_.isString(connection)) { + 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; diff --git a/packages/engines/mssql/MsSqlAnalyser.js b/packages/engines/mssql/MsSqlAnalyser.js index 22421f8f4..0c534df3c 100644 --- a/packages/engines/mssql/MsSqlAnalyser.js +++ b/packages/engines/mssql/MsSqlAnalyser.js @@ -1,42 +1,49 @@ -const fs = require('fs-extra'); -const fp = require('lodash/fp'); -const path = require('path'); -const _ = require('lodash'); +const fp = require("lodash/fp"); +const _ = require("lodash"); -const DatabaseAnalayser = require('../default/DatabaseAnalyser'); +const DatabaseAnalayser = require("../default/DatabaseAnalyser"); /** @returns {Promise} */ -async function loadQuery(name) { - return await fs.readFile(path.join(__dirname, name), 'utf-8'); +async function loadQuery(pool, name) { + 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) { const filtered = pkColumns.filter(byTableFilter(table)); if (filtered.length == 0) return undefined; return { - ..._.pick(filtered[0], ['constraintName', 'schemaName', 'pureName']), - constraintType: 'primaryKey', - columns: filtered.map(fp.pick('columnName')), + ..._.pick(filtered[0], ["constraintName", "schemaName", "pureName"]), + constraintType: "primaryKey", + columns: filtered.map(fp.pick("columnName")) }; } 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 => ({ constraintName, - constraintType: 'foreignKey', + constraintType: "foreignKey", ..._.pick(grouped[constraintName][0], [ - 'constraintName', - 'schemaName', - 'pureName', - 'refSchemaName', - 'refTableName', - 'updateAction', - 'deleteAction', + "constraintName", + "schemaName", + "pureName", + "refSchemaName", + "refTableName", + "updateAction", + "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, triggers = false ) { - let res = await loadQuery(resFileName); - res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null'); + let res = await loadQuery(this.pool, resFileName); + res = res.replace("=[OBJECT_ID_CONDITION]", " is not null"); return res; } async runAnalysis() { - const tables = await this.driver.query(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 fkColumns = await this.driver.query(this.pool, await this.createQuery('foreign_keys.sql')); + const tables = await this.driver.query( + 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 fkColumns = await this.driver.query( + this.pool, + await this.createQuery("foreign_keys.sql") + ); this.result.tables = tables.rows.map(table => ({ ...table, @@ -70,10 +89,10 @@ class MsSqlAnalyser extends DatabaseAnalayser { .map(({ isNullable, isIdentity, ...col }) => ({ ...col, notNull: !isNullable, - autoIncrement: !!isIdentity, + autoIncrement: !!isIdentity })), primaryKey: extractPrimaryKeys(table, pkColumns.rows), - foreignKeys: extractForeignKeys(table, fkColumns.rows), + foreignKeys: extractForeignKeys(table, fkColumns.rows) })); } } diff --git a/packages/engines/mssql/index.js b/packages/engines/mssql/index.js index 632bf9e5a..44f714620 100644 --- a/packages/engines/mssql/index.js +++ b/packages/engines/mssql/index.js @@ -12,14 +12,15 @@ const dialect = { /** @type {import('@dbgate/types').EngineDriver} */ const driver = { - async connect({ mssql }, { server, port, user, password, database }) { - const pool = await mssql.connect({ + async connect(nativeModules, { server, port, user, password, database }) { + const pool = await nativeModules.mssql.connect({ server, port, user, password, database }); + pool._nativeModules = nativeModules; return pool; }, async query(pool, sql) { diff --git a/packages/engines/mysql/MySqlAnalyser.js b/packages/engines/mysql/MySqlAnalyser.js index 17d2dc16b..041aa77ea 100644 --- a/packages/engines/mysql/MySqlAnalyser.js +++ b/packages/engines/mysql/MySqlAnalyser.js @@ -1,13 +1,14 @@ -const fs = require('fs-extra'); -const fp = require('lodash/fp'); -const path = require('path'); -const _ = require('lodash'); +const fp = require("lodash/fp"); +const _ = require("lodash"); -const DatabaseAnalayser = require('../default/DatabaseAnalyser'); +const DatabaseAnalayser = require("../default/DatabaseAnalyser"); /** @returns {Promise} */ -async function loadQuery(name) { - return await fs.readFile(path.join(__dirname, name), 'utf-8'); +async function loadQuery(pool, name) { + return await pool._nativeModules.fs.readFile( + pool._nativeModules.path.join(__dirname, name), + "utf-8" + ); } class MySqlAnalyser extends DatabaseAnalayser { @@ -23,14 +24,20 @@ class MySqlAnalyser extends DatabaseAnalayser { functions = false, triggers = false ) { - let res = await loadQuery(resFileName); - res = res.replace('=[OBJECT_NAME_CONDITION]', ' is not null'); - res = res.replace('#DATABASE#', this.pool._database_name); + let res = await loadQuery(this.pool, resFileName); + res = res.replace("=[OBJECT_NAME_CONDITION]", " is not null"); + res = res.replace("#DATABASE#", this.pool._database_name); return res; } async runAnalysis() { - const tables = await this.driver.query(this.pool, await this.createQuery('tables.sql')); - const columns = await this.driver.query(this.pool, await this.createQuery('columns.sql')); + const tables = await this.driver.query( + 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 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 }) => ({ ...col, notNull: !isNullable, - autoIncrement: extra && extra.toLowerCase().includes('auto_increment'), + autoIncrement: extra && extra.toLowerCase().includes("auto_increment") })), - foreignKeys: [], + foreignKeys: [] // primaryKey: extractPrimaryKeys(table, pkColumns.rows), // foreignKeys: extractForeignKeys(table, fkColumns.rows), })); diff --git a/packages/engines/mysql/index.js b/packages/engines/mysql/index.js index bcc1f1b9b..18938c756 100644 --- a/packages/engines/mysql/index.js +++ b/packages/engines/mysql/index.js @@ -11,8 +11,8 @@ const dialect = { /** @type {import('@dbgate/types').EngineDriver} */ const driver = { - async connect({ mysql }, { server, port, user, password, database }) { - const connection = mysql.createConnection({ + async connect(nativeModules, { server, port, user, password, database }) { + const connection = nativeModules.mysql.createConnection({ host: server, port, user, @@ -20,6 +20,7 @@ const driver = { database }); connection._database_name = database; + connection._nativeModules = nativeModules; return connection; }, async query(connection, sql) { diff --git a/packages/engines/package.json b/packages/engines/package.json index 312736c58..feaf30a21 100644 --- a/packages/engines/package.json +++ b/packages/engines/package.json @@ -10,8 +10,6 @@ "typescript": "^3.7.5" }, "dependencies": { - "fs-extra": "^8.1.0", - "lodash": "^4.17.15", - "path": "^0.12.7" + "lodash": "^4.17.15" } } diff --git a/packages/engines/postgres/PostgreAnalyser.js b/packages/engines/postgres/PostgreAnalyser.js index 126983afc..a7b3f2c39 100644 --- a/packages/engines/postgres/PostgreAnalyser.js +++ b/packages/engines/postgres/PostgreAnalyser.js @@ -1,13 +1,14 @@ -const fs = require('fs-extra'); -const fp = require('lodash/fp'); -const path = require('path'); -const _ = require('lodash'); +const fp = require("lodash/fp"); +const _ = require("lodash"); -const DatabaseAnalayser = require('../default/DatabaseAnalyser'); +const DatabaseAnalayser = require("../default/DatabaseAnalyser"); /** @returns {Promise} */ -async function loadQuery(name) { - return await fs.readFile(path.join(__dirname, name), 'utf-8'); +async function loadQuery(pool, name) { + return await pool._nativeModules.fs.readFile( + pool._nativeModules.path.join(__dirname, name), + "utf-8" + ); } class MySqlAnalyser extends DatabaseAnalayser { @@ -23,25 +24,34 @@ class MySqlAnalyser extends DatabaseAnalayser { functions = false, triggers = false ) { - let res = await loadQuery(resFileName); - res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null'); + let res = await loadQuery(this.pool, resFileName); + res = res.replace("=[OBJECT_ID_CONDITION]", " is not null"); return res; } async runAnalysis() { - const tables = await this.driver.query(this.pool, await this.createQuery('table_modifications.psql')); - const columns = await this.driver.query(this.pool, await this.createQuery('columns.psql')); + const tables = await this.driver.query( + 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 fkColumns = await this.driver.query(this.pool, await this.createQuery('foreign_keys.sql')); this.result.tables = tables.rows.map(table => ({ ...table, 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 }) => ({ ...col, - notNull: !isNullable, + notNull: !isNullable })), - foreignKeys: [], + foreignKeys: [] // primaryKey: extractPrimaryKeys(table, pkColumns.rows), // foreignKeys: extractForeignKeys(table, fkColumns.rows), })); diff --git a/packages/engines/postgres/index.js b/packages/engines/postgres/index.js index 2fbbbea49..f57cc9124 100644 --- a/packages/engines/postgres/index.js +++ b/packages/engines/postgres/index.js @@ -11,9 +11,10 @@ const dialect = { /** @type {import('@dbgate/types').EngineDriver} */ const driver = { - async connect({pg}, { server, port, user, password, database }) { - const client = new pg.Client({ host: server, port, user, password, database: database || 'postgres' }); + async connect(nativeModules, { server, port, user, password, database }) { + const client = new nativeModules.pg.Client({ host: server, port, user, password, database: database || 'postgres' }); await client.connect(); + client._nativeModules = nativeModules; return client; }, async query(client, sql) { diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index b2774cb66..527e08fba 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -4,7 +4,7 @@ import { SqlDumper } from "./dumper"; import { DatabaseInfo } from "./dbinfo"; 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; getVersion(pool: any): Promise<{ version: string }>; listDatabases( diff --git a/packages/web/package.json b/packages/web/package.json index 9237a7fe7..370b29afb 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -21,7 +21,8 @@ "resize-observer-polyfill": "^1.5.1", "socket.io-client": "^2.3.0", "styled-components": "^4.4.1", - "uuid": "^3.4.0" + "uuid": "^3.4.0", + "@dbgate/engines": "^0.1.0" }, "scripts": { "start": "cross-env PORT=5000 react-scripts start", diff --git a/packages/web/src/tabs/TableCreateScriptTab.js b/packages/web/src/tabs/TableCreateScriptTab.js index 66846834b..4025b6c5a 100644 --- a/packages/web/src/tabs/TableCreateScriptTab.js +++ b/packages/web/src/tabs/TableCreateScriptTab.js @@ -4,6 +4,7 @@ import styled from 'styled-components'; import theme from '../theme'; import AceEditor from 'react-ace'; import useDimensions from '../utility/useDimensions'; +import engines from '@dbgate/engines'; const Wrapper = styled.div` position: absolute; @@ -23,6 +24,11 @@ export default function TableCreateScriptTab({ conid, database, schemaName, pure 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 (