mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
ms sql analyse - table list
This commit is contained in:
parent
c819aac098
commit
69322a4e41
47
api/src/controllers/databaseConnections.js
Normal file
47
api/src/controllers/databaseConnections.js
Normal file
@ -0,0 +1,47 @@
|
||||
const fp = require('lodash/fp');
|
||||
const connections = require('./connections');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const DatabaseAnalyser = require('../engines/default/DatabaseAnalyser')
|
||||
|
||||
module.exports = {
|
||||
/** @type {import('../types').OpenedDatabaseConnection[]} */
|
||||
opened: [],
|
||||
|
||||
handle_structure(id, database, { structure }) {
|
||||
const existing = this.opened.find(x => x.id == id && x.database == database);
|
||||
if (!existing) return;
|
||||
existing.structure = structure;
|
||||
socket.emit(`database-structure-changed-${id}-${database}`);
|
||||
},
|
||||
handle_error(id, { error }) {
|
||||
console.log(error);
|
||||
},
|
||||
|
||||
async ensureOpened(id, database) {
|
||||
const existing = this.opened.find(x => x.id == id && x.database == database);
|
||||
if (existing) return existing;
|
||||
const connection = await connections.get({ id });
|
||||
const subprocess = fork(`${__dirname}/../proc/databaseConnectionProcess.js`);
|
||||
const newOpened = {
|
||||
id,
|
||||
database,
|
||||
subprocess,
|
||||
structure: DatabaseAnalyser.createEmptyStructure(),
|
||||
connection,
|
||||
};
|
||||
this.opened.push(newOpened);
|
||||
// @ts-ignore
|
||||
subprocess.on('message', ({ msgtype, ...message }) => {
|
||||
this[`handle_${msgtype}`](id, database, message);
|
||||
});
|
||||
subprocess.send({ msgtype: 'connect', ...connection });
|
||||
return newOpened;
|
||||
},
|
||||
|
||||
listTables_meta: 'get',
|
||||
async listTables({ id, database }) {
|
||||
const opened = await this.ensureOpened(id, database);
|
||||
return opened.structure.tables; // .map(fp.pick(['tableName', 'schemaName']));
|
||||
},
|
||||
};
|
@ -1,14 +1,19 @@
|
||||
|
||||
class DatabaseAnalyser {
|
||||
/**
|
||||
*
|
||||
* @param {import('../default/types').EngineDriver} driver
|
||||
* @param {import('../../types').EngineDriver} driver
|
||||
*/
|
||||
constructor(pool, driver) {
|
||||
this.pool = pool;
|
||||
this.driver = driver;
|
||||
this.result = DatabaseAnalyser.createEmptyStructure();
|
||||
}
|
||||
runAnalysis() {}
|
||||
async runAnalysis() {}
|
||||
}
|
||||
|
||||
/** @returns {import('../../types').DatabaseInfo} */
|
||||
DatabaseAnalyser.createEmptyStructure = () => ({
|
||||
tables: [],
|
||||
});
|
||||
|
||||
module.exports = DatabaseAnalyser;
|
||||
|
@ -1,8 +0,0 @@
|
||||
export interface EngineDriver {
|
||||
connect({ server, port, user, password });
|
||||
query(pool, sql: string): [];
|
||||
getVersion(pool): string;
|
||||
listDatabases(pool): [{ name: string }];
|
||||
analyseFull(pool);
|
||||
analyseIncremental(pool);
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
module.exports = connection => {
|
||||
|
||||
/** @return {import('../types').EngineDriver} */
|
||||
function getDriver(connection) {
|
||||
const { engine } = connection;
|
||||
return require(`./${engine}`);
|
||||
};
|
||||
|
||||
}
|
||||
module.exports = getDriver;
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const DatabaseAnalayser = require('../default/DatabaseAnalyser');
|
||||
|
||||
/** @returns {Promise<string>} */
|
||||
async function loadQuery(name) {
|
||||
return await fs.readFile(path.join(__dirname, name), 'utf-8');
|
||||
}
|
||||
@ -13,8 +13,27 @@ class MsSqlAnalyser extends DatabaseAnalayser {
|
||||
super(pool, driver);
|
||||
}
|
||||
|
||||
async createQuery(
|
||||
resFileName,
|
||||
tables = false,
|
||||
views = false,
|
||||
procedures = false,
|
||||
functions = false,
|
||||
triggers = false
|
||||
) {
|
||||
let res = await loadQuery(resFileName);
|
||||
res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null');
|
||||
return res;
|
||||
}
|
||||
async runAnalysis() {
|
||||
const tables = this.driver.query(this.pool, await loadQuery('tables.sql'));
|
||||
const tables = await this.driver.query(this.pool, await this.createQuery('tables.sql'));
|
||||
// for (const table of tables) {
|
||||
// table.name = {
|
||||
// schema: table.schemaName,
|
||||
// name: table.tableName,
|
||||
// };
|
||||
// }
|
||||
this.result.tables = tables;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@ const mssql = require('mssql');
|
||||
const MsSqlAnalyser = require('./MsSqlAnalyser');
|
||||
|
||||
module.exports = {
|
||||
async connect({ server, port, user, password }) {
|
||||
const pool = await mssql.connect({ server, port, user, password });
|
||||
async connect({ server, port, user, password, database }) {
|
||||
const pool = await mssql.connect({ server, port, user, password, database });
|
||||
return pool;
|
||||
},
|
||||
async query(pool, sql) {
|
||||
@ -19,9 +19,9 @@ module.exports = {
|
||||
return res;
|
||||
},
|
||||
async analyseFull(pool) {
|
||||
|
||||
},
|
||||
async analyseIncremental(pool) {
|
||||
|
||||
const analyser = new MsSqlAnalyser(pool, this);
|
||||
await analyser.runAnalysis();
|
||||
return analyser.result;
|
||||
},
|
||||
async analyseIncremental(pool) {},
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
select
|
||||
o.name as tableName, s.name as schemaName, o.objectId,
|
||||
o.createDate, o.modifyDate
|
||||
o.name as tableName, s.name as schemaName, o.object_id,
|
||||
o.create_date, o.modify_date
|
||||
from sys.tables o
|
||||
inner join sys.schemas s on o.schema_id = s.schema_id
|
||||
where o.object_id =[OBJECT_ID_CONDITION]
|
||||
|
@ -1,8 +1,8 @@
|
||||
const mysql = require('mysql');
|
||||
|
||||
module.exports = {
|
||||
async connect({ server, port, user, password }) {
|
||||
const connection = mysql.createConnection({ host: server, port, user, password });
|
||||
async connect({ server, port, user, password, database }) {
|
||||
const connection = mysql.createConnection({ host: server, port, user, password, database });
|
||||
return connection;
|
||||
},
|
||||
async query(connection, sql) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
module.exports = {
|
||||
async connect({ server, port, user, password }) {
|
||||
const client = new Client({ host: server, port, user, password, database: 'postgres' });
|
||||
async connect({ server, port, user, password, database }) {
|
||||
const client = new Client({ host: server, port, user, password, database: database || 'postgres' });
|
||||
await client.connect();
|
||||
return client;
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ const io = require('socket.io');
|
||||
const useController = require('./utility/useController');
|
||||
const connections = require('./controllers/connections');
|
||||
const serverConnections = require('./controllers/serverConnections');
|
||||
const databaseConnections = require('./controllers/databaseConnections');
|
||||
const socket = require('./utility/socket');
|
||||
|
||||
const app = express();
|
||||
@ -23,5 +24,6 @@ app.get('/', (req, res) => {
|
||||
|
||||
useController(app, '/connections', connections);
|
||||
useController(app, '/server-connections', serverConnections);
|
||||
useController(app, '/database-connections', databaseConnections);
|
||||
|
||||
server.listen(3000);
|
||||
|
37
api/src/proc/databaseConnectionProcess.js
Normal file
37
api/src/proc/databaseConnectionProcess.js
Normal file
@ -0,0 +1,37 @@
|
||||
const engines = require('../engines');
|
||||
|
||||
let systemConnection;
|
||||
let storedConnection;
|
||||
|
||||
async function handleFullRefresh() {
|
||||
const driver = engines(storedConnection);
|
||||
const structure = await driver.analyseFull(systemConnection);
|
||||
console.log('SENDING STRUCTURE', structure);
|
||||
process.send({ msgtype: 'structure', structure });
|
||||
}
|
||||
|
||||
async function handleConnect(connection, database) {
|
||||
storedConnection = connection;
|
||||
|
||||
const driver = engines(storedConnection);
|
||||
systemConnection = await driver.connect({ ...storedConnection, database });
|
||||
handleFullRefresh();
|
||||
setInterval(handleFullRefresh, 30 * 1000);
|
||||
}
|
||||
|
||||
const messageHandlers = {
|
||||
connect: handleConnect,
|
||||
};
|
||||
|
||||
async function handleMessage({ msgtype, database, ...other }) {
|
||||
const handler = messageHandlers[msgtype];
|
||||
await handler(other, database);
|
||||
}
|
||||
|
||||
process.on('message', async message => {
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (e) {
|
||||
process.send({ msgtype: 'error', error: e.message });
|
||||
}
|
||||
});
|
29
api/src/types.ts
Normal file
29
api/src/types.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export interface EngineDriver {
|
||||
connect({ server, port, user, password });
|
||||
query(pool, sql: string): Promise<any[]>;
|
||||
getVersion(pool): Promise<string>;
|
||||
listDatabases(pool): Promise<{ name: string }[]>;
|
||||
analyseFull(pool): Promise<void>;
|
||||
analyseIncremental(pool): Promise<void>;
|
||||
}
|
||||
|
||||
// export interface NameWithSchema {
|
||||
// schema: string;
|
||||
// name: string;
|
||||
// }
|
||||
|
||||
export interface TableInfo {
|
||||
// name: NameWithSchema;
|
||||
tableName: string;
|
||||
schemaName: string;
|
||||
}
|
||||
|
||||
export interface DatabaseInfo {
|
||||
tables: TableInfo[];
|
||||
}
|
||||
|
||||
export interface OpenedDatabaseConnection {
|
||||
id: string;
|
||||
database: string;
|
||||
structure: DatabaseInfo;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import React from 'react';
|
||||
import theme from './theme';
|
||||
import styled from 'styled-components';
|
||||
@ -9,10 +11,7 @@ import WidgetContainer from './widgets/WidgetContainer';
|
||||
const BodyDiv = styled.div`
|
||||
position: fixed;
|
||||
top: ${theme.tabsPanel.height}px;
|
||||
left: ${props =>
|
||||
theme.widgetMenu.iconSize +
|
||||
// @ts-ignore
|
||||
props.leftPanelWidth}px;
|
||||
left: ${props => theme.widgetMenu.iconSize + props.leftPanelWidth}px;
|
||||
bottom: ${theme.statusBar.height}px;
|
||||
right: 0;
|
||||
background-color: ${theme.mainArea.background};
|
||||
@ -41,10 +40,7 @@ const TabsPanel = styled.div`
|
||||
display: flex;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: ${props =>
|
||||
theme.widgetMenu.iconSize +
|
||||
// @ts-ignore
|
||||
props.leftPanelWidth}px;
|
||||
left: ${props => theme.widgetMenu.iconSize + props.leftPanelWidth}px;
|
||||
height: ${theme.tabsPanel.height}px;
|
||||
right: 0;
|
||||
background-color: ${theme.tabsPanel.background};
|
||||
@ -72,18 +68,10 @@ export default function Screen({ children = undefined }) {
|
||||
<WidgetContainer />
|
||||
</LeftPanel>
|
||||
)}
|
||||
<TabsPanel
|
||||
// @ts-ignore
|
||||
leftPanelWidth={leftPanelWidth}
|
||||
>
|
||||
<TabsPanel leftPanelWidth={leftPanelWidth}>
|
||||
<FilesTabsPanel></FilesTabsPanel>
|
||||
</TabsPanel>
|
||||
<BodyDiv
|
||||
// @ts-ignore
|
||||
leftPanelWidth={leftPanelWidth}
|
||||
>
|
||||
{children}
|
||||
</BodyDiv>
|
||||
<BodyDiv leftPanelWidth={leftPanelWidth}>{children}</BodyDiv>
|
||||
<StausBar></StausBar>
|
||||
</>
|
||||
);
|
||||
|
@ -37,21 +37,56 @@ function SubDatabaseList({ data }) {
|
||||
return <AppObjectList list={databases} makeAppObj={databaseAppObject} onObjectClick={handleDatabaseClick} />;
|
||||
}
|
||||
|
||||
export default function DatabaseWidget() {
|
||||
const db = useCurrentDatabase();
|
||||
function ConnectionList() {
|
||||
const modalState = useModalState();
|
||||
const connections = useFetch({
|
||||
url: 'connections/list',
|
||||
reloadTrigger: 'connection-list-changed',
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<ConnectionModal modalState={modalState} />
|
||||
<button onClick={modalState.open}>Add connection</button>
|
||||
<AppObjectList list={connections} makeAppObj={connectionAppObject} SubItems={SubDatabaseList} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SqlObjectList({ id, database }) {
|
||||
const tables =
|
||||
useFetch({
|
||||
url: `database-connections/list-tables?id=${id}&database=${database}`,
|
||||
reloadTrigger: `database-structure-changed-${id}-${database}`,
|
||||
}) || [];
|
||||
return (
|
||||
<>
|
||||
{tables.map(({ tableName, schemaName }) => (
|
||||
<div key={`${schemaName}.${tableName}`}>{tableName}</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SqlObjectListWrapper() {
|
||||
const db = useCurrentDatabase();
|
||||
|
||||
if (!db) return <div>(Choose database)</div>;
|
||||
const { name, connection } = db;
|
||||
|
||||
return <SqlObjectList id={connection._id} database={name} />;
|
||||
// return <div>tables of {db && db.name}</div>
|
||||
// return <div>tables of {JSON.stringify(db)}</div>
|
||||
}
|
||||
|
||||
export default function DatabaseWidget() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<InnerContainer>
|
||||
<ConnectionModal modalState={modalState} />
|
||||
<button onClick={modalState.open}>Add connection</button>
|
||||
<AppObjectList list={connections} makeAppObj={connectionAppObject} SubItems={SubDatabaseList} />
|
||||
<ConnectionList />
|
||||
</InnerContainer>
|
||||
<InnerContainer>
|
||||
<SqlObjectListWrapper />
|
||||
</InnerContainer>
|
||||
<InnerContainer>tables of {db && db.name}</InnerContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user