ms sql analyse - table list

This commit is contained in:
Jan Prochazka 2020-01-19 21:01:48 +01:00
parent c819aac098
commit 69322a4e41
14 changed files with 209 additions and 51 deletions

View 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']));
},
};

View File

@ -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;

View File

@ -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);
}

View File

@ -1,4 +1,8 @@
module.exports = connection => {
/** @return {import('../types').EngineDriver} */
function getDriver(connection) {
const { engine } = connection;
return require(`./${engine}`);
};
}
module.exports = getDriver;

View File

@ -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;
}
}

View File

@ -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) {},
};

View File

@ -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]

View File

@ -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) {

View File

@ -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;
},

View File

@ -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);

View 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
View 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;
}

View File

@ -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>
</>
);

View File

@ -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>
);
}